// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

package api4

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"sort"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/mattermost/mattermost-server/v6/app"
	"github.com/mattermost/mattermost-server/v6/model"
	"github.com/mattermost/mattermost-server/v6/plugin/plugintest/mock"
	"github.com/mattermost/mattermost-server/v6/store/storetest/mocks"
	"github.com/mattermost/mattermost-server/v6/utils/testutils"
)

func TestCreateChannel(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client
	team := th.BasicTeam

	channel := &model.Channel{DisplayName: "Test API Name", Name: GenerateTestChannelName(), Type: model.ChannelTypeOpen, TeamId: team.Id}
	private := &model.Channel{DisplayName: "Test API Name", Name: GenerateTestChannelName(), Type: model.ChannelTypePrivate, TeamId: team.Id}

	rchannel, resp, err := client.CreateChannel(channel)
	require.NoError(t, err)
	CheckCreatedStatus(t, resp)

	require.Equal(t, channel.Name, rchannel.Name, "names did not match")
	require.Equal(t, channel.DisplayName, rchannel.DisplayName, "display names did not match")
	require.Equal(t, channel.TeamId, rchannel.TeamId, "team ids did not match")

	rprivate, _, err := client.CreateChannel(private)
	require.NoError(t, err)

	require.Equal(t, private.Name, rprivate.Name, "names did not match")
	require.Equal(t, model.ChannelTypePrivate, rprivate.Type, "wrong channel type")
	require.Equal(t, th.BasicUser.Id, rprivate.CreatorId, "wrong creator id")

	_, resp, err = client.CreateChannel(channel)
	CheckErrorID(t, err, "store.sql_channel.save_channel.exists.app_error")
	CheckBadRequestStatus(t, resp)

	direct := &model.Channel{DisplayName: "Test API Name", Name: GenerateTestChannelName(), Type: model.ChannelTypeDirect, TeamId: team.Id}
	_, resp, err = client.CreateChannel(direct)
	CheckErrorID(t, err, "api.channel.create_channel.direct_channel.app_error")
	CheckBadRequestStatus(t, resp)

	client.Logout()
	_, resp, err = client.CreateChannel(channel)
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	userNotOnTeam := th.CreateUser()
	client.Login(userNotOnTeam.Email, userNotOnTeam.Password)

	_, resp, err = client.CreateChannel(channel)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	_, resp, err = client.CreateChannel(private)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	// Check the appropriate permissions are enforced.
	defaultRolePermissions := th.SaveDefaultRolePermissions()
	defer func() {
		th.RestoreDefaultRolePermissions(defaultRolePermissions)
	}()

	th.AddPermissionToRole(model.PermissionCreatePublicChannel.Id, model.TeamUserRoleId)
	th.AddPermissionToRole(model.PermissionCreatePrivateChannel.Id, model.TeamUserRoleId)

	th.LoginBasic()

	channel.Name = GenerateTestChannelName()
	_, _, err = client.CreateChannel(channel)
	require.NoError(t, err)

	private.Name = GenerateTestChannelName()
	_, _, err = client.CreateChannel(private)
	require.NoError(t, err)

	th.AddPermissionToRole(model.PermissionCreatePublicChannel.Id, model.TeamAdminRoleId)
	th.AddPermissionToRole(model.PermissionCreatePrivateChannel.Id, model.TeamAdminRoleId)
	th.RemovePermissionFromRole(model.PermissionCreatePublicChannel.Id, model.TeamUserRoleId)
	th.RemovePermissionFromRole(model.PermissionCreatePrivateChannel.Id, model.TeamUserRoleId)

	_, resp, err = client.CreateChannel(channel)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	_, resp, err = client.CreateChannel(private)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	th.LoginTeamAdmin()

	channel.Name = GenerateTestChannelName()
	_, _, err = client.CreateChannel(channel)
	require.NoError(t, err)

	private.Name = GenerateTestChannelName()
	_, _, err = client.CreateChannel(private)
	require.NoError(t, err)

	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
		channel.Name = GenerateTestChannelName()
		_, _, err = client.CreateChannel(channel)
		require.NoError(t, err)

		private.Name = GenerateTestChannelName()
		_, _, err = client.CreateChannel(private)
		require.NoError(t, err)
	})

	// Test posting Garbage
	r, err := client.DoAPIPost("/channels", "garbage")
	require.Error(t, err, "expected error")
	require.Equal(t, http.StatusBadRequest, r.StatusCode, "Expected 400 Bad Request")

	// Test GroupConstrained flag
	groupConstrainedChannel := &model.Channel{DisplayName: "Test API Name", Name: GenerateTestChannelName(), Type: model.ChannelTypeOpen, TeamId: team.Id, GroupConstrained: model.NewBool(true)}
	rchannel, _, err = client.CreateChannel(groupConstrainedChannel)
	require.NoError(t, err)

	require.Equal(t, *groupConstrainedChannel.GroupConstrained, *rchannel.GroupConstrained, "GroupConstrained flags do not match")
}

func TestUpdateChannel(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client
	team := th.BasicTeam

	channel := &model.Channel{DisplayName: "Test API Name", Name: GenerateTestChannelName(), Type: model.ChannelTypeOpen, TeamId: team.Id}
	private := &model.Channel{DisplayName: "Test API Name", Name: GenerateTestChannelName(), Type: model.ChannelTypePrivate, TeamId: team.Id}

	channel, _, _ = client.CreateChannel(channel)
	private, _, _ = client.CreateChannel(private)

	//Update a open channel
	channel.DisplayName = "My new display name"
	channel.Header = "My fancy header"
	channel.Purpose = "Mattermost ftw!"

	newChannel, _, err := client.UpdateChannel(channel)
	require.NoError(t, err)

	require.Equal(t, channel.DisplayName, newChannel.DisplayName, "Update failed for DisplayName")
	require.Equal(t, channel.Header, newChannel.Header, "Update failed for Header")
	require.Equal(t, channel.Purpose, newChannel.Purpose, "Update failed for Purpose")

	// Test GroupConstrained flag
	channel.GroupConstrained = model.NewBool(true)
	rchannel, resp, err := client.UpdateChannel(channel)
	require.NoError(t, err)
	CheckOKStatus(t, resp)

	require.Equal(t, *channel.GroupConstrained, *rchannel.GroupConstrained, "GroupConstrained flags do not match")

	//Update a private channel
	private.DisplayName = "My new display name for private channel"
	private.Header = "My fancy private header"
	private.Purpose = "Mattermost ftw! in private mode"

	newPrivateChannel, _, err := client.UpdateChannel(private)
	require.NoError(t, err)

	require.Equal(t, private.DisplayName, newPrivateChannel.DisplayName, "Update failed for DisplayName in private channel")
	require.Equal(t, private.Header, newPrivateChannel.Header, "Update failed for Header in private channel")
	require.Equal(t, private.Purpose, newPrivateChannel.Purpose, "Update failed for Purpose in private channel")

	//Test updating default channel's name and returns error
	defaultChannel, _ := th.App.GetChannelByName(th.Context, model.DefaultChannelName, team.Id, false)
	defaultChannel.Name = "testing"
	_, resp, err = client.UpdateChannel(defaultChannel)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	// Test that changing the type fails and returns error
	private.Type = model.ChannelTypeOpen
	_, resp, err = client.UpdateChannel(private)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	// Test that keeping the same type succeeds

	private.Type = model.ChannelTypePrivate
	_, _, err = client.UpdateChannel(private)
	require.NoError(t, err)

	//Non existing channel
	channel1 := &model.Channel{DisplayName: "Test API Name for apiv4", Name: GenerateTestChannelName(), Type: model.ChannelTypeOpen, TeamId: team.Id}
	_, resp, err = client.UpdateChannel(channel1)
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	//Try to update with not logged user
	client.Logout()
	_, resp, err = client.UpdateChannel(channel)
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	//Try to update using another user
	user := th.CreateUser()
	client.Login(user.Email, user.Password)

	channel.DisplayName = "Should not update"
	_, resp, err = client.UpdateChannel(channel)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	// Test updating the header of someone else's GM channel.
	user1 := th.CreateUser()
	user2 := th.CreateUser()
	user3 := th.CreateUser()

	groupChannel, _, err := client.CreateGroupChannel([]string{user1.Id, user2.Id})
	require.NoError(t, err)

	groupChannel.Header = "lolololol"
	client.Logout()
	client.Login(user3.Email, user3.Password)
	_, resp, err = client.UpdateChannel(groupChannel)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	// Test updating the header of someone else's GM channel.
	client.Logout()
	client.Login(user.Email, user.Password)

	directChannel, _, err := client.CreateDirectChannel(user.Id, user1.Id)
	require.NoError(t, err)

	directChannel.Header = "lolololol"
	client.Logout()
	client.Login(user3.Email, user3.Password)
	_, resp, err = client.UpdateChannel(directChannel)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)
}

func TestPatchChannel(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client
	team := th.BasicTeam

	patch := &model.ChannelPatch{
		Name:        new(string),
		DisplayName: new(string),
		Header:      new(string),
		Purpose:     new(string),
	}
	*patch.Name = model.NewId()
	*patch.DisplayName = model.NewId()
	*patch.Header = model.NewId()
	*patch.Purpose = model.NewId()

	channel, _, err := client.PatchChannel(th.BasicChannel.Id, patch)
	require.NoError(t, err)

	require.Equal(t, *patch.Name, channel.Name, "do not match")
	require.Equal(t, *patch.DisplayName, channel.DisplayName, "do not match")
	require.Equal(t, *patch.Header, channel.Header, "do not match")
	require.Equal(t, *patch.Purpose, channel.Purpose, "do not match")

	patch.Name = nil
	oldName := channel.Name
	channel, _, err = client.PatchChannel(th.BasicChannel.Id, patch)
	require.NoError(t, err)

	require.Equal(t, oldName, channel.Name, "should not have updated")

	//Test updating default channel's name and returns error
	defaultChannel, _ := th.App.GetChannelByName(th.Context, model.DefaultChannelName, team.Id, false)
	defaultChannelPatch := &model.ChannelPatch{
		Name: new(string),
	}
	*defaultChannelPatch.Name = "testing"
	_, resp, err := client.PatchChannel(defaultChannel.Id, defaultChannelPatch)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	// Test GroupConstrained flag
	patch.GroupConstrained = model.NewBool(true)
	rchannel, resp, err := client.PatchChannel(th.BasicChannel.Id, patch)
	require.NoError(t, err)
	CheckOKStatus(t, resp)

	require.Equal(t, *rchannel.GroupConstrained, *patch.GroupConstrained, "GroupConstrained flags do not match")
	patch.GroupConstrained = nil

	_, resp, err = client.PatchChannel("junk", patch)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	_, resp, err = client.PatchChannel(model.NewId(), patch)
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	user := th.CreateUser()
	client.Login(user.Email, user.Password)
	_, resp, err = client.PatchChannel(th.BasicChannel.Id, patch)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
		_, _, err = client.PatchChannel(th.BasicChannel.Id, patch)
		require.NoError(t, err)

		_, _, err = client.PatchChannel(th.BasicPrivateChannel.Id, patch)
		require.NoError(t, err)
	})

	// Test updating the header of someone else's GM channel.
	user1 := th.CreateUser()
	user2 := th.CreateUser()
	user3 := th.CreateUser()

	groupChannel, _, err := client.CreateGroupChannel([]string{user1.Id, user2.Id})
	require.NoError(t, err)

	client.Logout()
	client.Login(user3.Email, user3.Password)

	channelPatch := &model.ChannelPatch{}
	channelPatch.Header = new(string)
	*channelPatch.Header = "lolololol"

	_, resp, err = client.PatchChannel(groupChannel.Id, channelPatch)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	// Test updating the header of someone else's GM channel.
	client.Logout()
	client.Login(user.Email, user.Password)

	directChannel, _, err := client.CreateDirectChannel(user.Id, user1.Id)
	require.NoError(t, err)

	client.Logout()
	client.Login(user3.Email, user3.Password)
	_, resp, err = client.PatchChannel(directChannel.Id, channelPatch)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)
}

func TestChannelUnicodeNames(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client
	team := th.BasicTeam

	t.Run("create channel unicode", func(t *testing.T) {
		channel := &model.Channel{
			Name:        "\u206cenglish\u206dchannel",
			DisplayName: "The \u206cEnglish\u206d Channel",
			Type:        model.ChannelTypeOpen,
			TeamId:      team.Id}

		rchannel, resp, err := client.CreateChannel(channel)
		require.NoError(t, err)
		CheckCreatedStatus(t, resp)

		require.Equal(t, "englishchannel", rchannel.Name, "bad unicode should be filtered from name")
		require.Equal(t, "The English Channel", rchannel.DisplayName, "bad unicode should be filtered from display name")
	})

	t.Run("update channel unicode", func(t *testing.T) {
		channel := &model.Channel{
			DisplayName: "Test API Name",
			Name:        GenerateTestChannelName(),
			Type:        model.ChannelTypeOpen,
			TeamId:      team.Id,
		}
		channel, _, _ = client.CreateChannel(channel)

		channel.Name = "\u206ahistorychannel"
		channel.DisplayName = "UFO's and \ufff9stuff\ufffb."

		newChannel, _, err := client.UpdateChannel(channel)
		require.NoError(t, err)

		require.Equal(t, "historychannel", newChannel.Name, "bad unicode should be filtered from name")
		require.Equal(t, "UFO's and stuff.", newChannel.DisplayName, "bad unicode should be filtered from display name")
	})

	t.Run("patch channel unicode", func(t *testing.T) {
		patch := &model.ChannelPatch{
			Name:        new(string),
			DisplayName: new(string),
			Header:      new(string),
			Purpose:     new(string),
		}
		*patch.Name = "\u206ecommunitychannel\u206f"
		*patch.DisplayName = "Natalie Tran's \ufffcAwesome Channel"

		channel, _, err := client.PatchChannel(th.BasicChannel.Id, patch)
		require.NoError(t, err)

		require.Equal(t, "communitychannel", channel.Name, "bad unicode should be filtered from name")
		require.Equal(t, "Natalie Tran's Awesome Channel", channel.DisplayName, "bad unicode should be filtered from display name")
	})
}

func TestCreateDirectChannel(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client
	user1 := th.BasicUser
	user2 := th.BasicUser2
	user3 := th.CreateUser()

	dm, _, err := client.CreateDirectChannel(user1.Id, user2.Id)
	require.NoError(t, err)

	channelName := ""
	if user2.Id > user1.Id {
		channelName = user1.Id + "__" + user2.Id
	} else {
		channelName = user2.Id + "__" + user1.Id
	}

	require.Equal(t, channelName, dm.Name, "dm name didn't match")

	_, resp, err := client.CreateDirectChannel("junk", user2.Id)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	_, resp, err = client.CreateDirectChannel(user1.Id, model.NewId())
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	_, resp, err = client.CreateDirectChannel(model.NewId(), user1.Id)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	_, resp, err = client.CreateDirectChannel(model.NewId(), user2.Id)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	r, err := client.DoAPIPost("/channels/direct", "garbage")
	require.Error(t, err)
	require.Equal(t, http.StatusBadRequest, r.StatusCode)

	_, _, err = th.SystemAdminClient.CreateDirectChannel(user3.Id, user2.Id)
	require.NoError(t, err)

	// Normal client should not be allowed to create a direct channel if users are
	// restricted to messaging members of their own team
	th.App.UpdateConfig(func(cfg *model.Config) {
		*cfg.TeamSettings.RestrictDirectMessage = model.DirectMessageTeam
	})
	user4 := th.CreateUser()
	_, resp, err = th.Client.CreateDirectChannel(user1.Id, user4.Id)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)
	th.LinkUserToTeam(user4, th.BasicTeam)
	_, _, err = th.Client.CreateDirectChannel(user1.Id, user4.Id)
	require.NoError(t, err)

	client.Logout()
	_, resp, err = client.CreateDirectChannel(model.NewId(), user2.Id)
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)
}

func TestCreateDirectChannelAsGuest(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client
	user1 := th.BasicUser

	enableGuestAccounts := *th.App.Config().GuestAccountsSettings.Enable
	defer func() {
		th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = enableGuestAccounts })
		th.App.Srv().RemoveLicense()
	}()
	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = true })
	th.App.Srv().SetLicense(model.NewTestLicense())

	id := model.NewId()
	guest := &model.User{
		Email:         "success+" + id + "@simulator.amazonses.com",
		Username:      "un_" + id,
		Nickname:      "nn_" + id,
		Password:      "Password1",
		EmailVerified: true,
	}
	guest, appErr := th.App.CreateGuest(th.Context, guest)
	require.Nil(t, appErr)

	_, _, err := client.Login(guest.Username, "Password1")
	require.NoError(t, err)

	t.Run("Try to created DM with not visible user", func(t *testing.T) {
		var resp *model.Response
		_, resp, err = client.CreateDirectChannel(guest.Id, user1.Id)
		require.Error(t, err)
		CheckForbiddenStatus(t, resp)

		_, resp, err = client.CreateDirectChannel(user1.Id, guest.Id)
		require.Error(t, err)
		CheckForbiddenStatus(t, resp)
	})

	t.Run("Creating DM with visible user", func(t *testing.T) {
		th.LinkUserToTeam(guest, th.BasicTeam)
		th.AddUserToChannel(guest, th.BasicChannel)

		_, _, err = client.CreateDirectChannel(guest.Id, user1.Id)
		require.NoError(t, err)
	})
}

func TestDeleteDirectChannel(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client
	user := th.BasicUser
	user2 := th.BasicUser2

	rgc, resp, err := client.CreateDirectChannel(user.Id, user2.Id)
	require.NoError(t, err)
	CheckCreatedStatus(t, resp)
	require.NotNil(t, rgc, "should have created a direct channel")

	_, err = client.DeleteChannel(rgc.Id)
	CheckErrorID(t, err, "api.channel.delete_channel.type.invalid")
}

func TestCreateGroupChannel(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client
	user := th.BasicUser
	user2 := th.BasicUser2
	user3 := th.CreateUser()

	userIds := []string{user.Id, user2.Id, user3.Id}

	rgc, resp, err := client.CreateGroupChannel(userIds)
	require.NoError(t, err)
	CheckCreatedStatus(t, resp)

	require.NotNil(t, rgc, "should have created a group channel")
	require.Equal(t, model.ChannelTypeGroup, rgc.Type, "should have created a channel of group type")

	m, _ := th.App.GetChannelMembersPage(th.Context, rgc.Id, 0, 10)
	require.Len(t, m, 3, "should have 3 channel members")

	// saving duplicate group channel
	rgc2, _, err := client.CreateGroupChannel([]string{user3.Id, user2.Id})
	require.NoError(t, err)
	require.Equal(t, rgc.Id, rgc2.Id, "should have returned existing channel")

	m2, _ := th.App.GetChannelMembersPage(th.Context, rgc2.Id, 0, 10)
	require.ElementsMatch(t, m, m2)

	_, resp, err = client.CreateGroupChannel([]string{user2.Id})
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	user4 := th.CreateUser()
	user5 := th.CreateUser()
	user6 := th.CreateUser()
	user7 := th.CreateUser()
	user8 := th.CreateUser()
	user9 := th.CreateUser()

	rgc, resp, err = client.CreateGroupChannel([]string{user.Id, user2.Id, user3.Id, user4.Id, user5.Id, user6.Id, user7.Id, user8.Id, user9.Id})
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)
	require.Nil(t, rgc)

	_, resp, err = client.CreateGroupChannel([]string{user.Id, user2.Id, user3.Id, GenerateTestId()})
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	_, resp, err = client.CreateGroupChannel([]string{user.Id, user2.Id, user3.Id, "junk"})
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	client.Logout()

	_, resp, err = client.CreateGroupChannel(userIds)
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	_, _, err = th.SystemAdminClient.CreateGroupChannel(userIds)
	require.NoError(t, err)
}

func TestCreateGroupChannelAsGuest(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client
	user1 := th.BasicUser
	user2 := th.BasicUser2
	user3 := th.CreateUser()
	user4 := th.CreateUser()
	user5 := th.CreateUser()
	th.LinkUserToTeam(user2, th.BasicTeam)
	th.AddUserToChannel(user2, th.BasicChannel)
	th.LinkUserToTeam(user3, th.BasicTeam)
	th.AddUserToChannel(user3, th.BasicChannel)

	enableGuestAccounts := *th.App.Config().GuestAccountsSettings.Enable
	defer func() {
		th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = enableGuestAccounts })
		th.App.Srv().RemoveLicense()
	}()
	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = true })
	th.App.Srv().SetLicense(model.NewTestLicense())

	id := model.NewId()
	guest := &model.User{
		Email:         "success+" + id + "@simulator.amazonses.com",
		Username:      "un_" + id,
		Nickname:      "nn_" + id,
		Password:      "Password1",
		EmailVerified: true,
	}
	guest, appErr := th.App.CreateGuest(th.Context, guest)
	require.Nil(t, appErr)

	_, _, err := client.Login(guest.Username, "Password1")
	require.NoError(t, err)

	var resp *model.Response

	t.Run("Try to created GM with not visible users", func(t *testing.T) {
		_, resp, err = client.CreateGroupChannel([]string{guest.Id, user1.Id, user2.Id, user3.Id})
		require.Error(t, err)
		CheckForbiddenStatus(t, resp)

		_, resp, err = client.CreateGroupChannel([]string{user1.Id, user2.Id, guest.Id, user3.Id})
		require.Error(t, err)
		CheckForbiddenStatus(t, resp)
	})

	t.Run("Try to created GM with visible and not visible users", func(t *testing.T) {
		th.LinkUserToTeam(guest, th.BasicTeam)
		th.AddUserToChannel(guest, th.BasicChannel)

		_, resp, err = client.CreateGroupChannel([]string{guest.Id, user1.Id, user3.Id, user4.Id, user5.Id})
		require.Error(t, err)
		CheckForbiddenStatus(t, resp)

		_, resp, err = client.CreateGroupChannel([]string{user1.Id, user2.Id, guest.Id, user4.Id, user5.Id})
		require.Error(t, err)
		CheckForbiddenStatus(t, resp)
	})

	t.Run("Creating GM with visible users", func(t *testing.T) {
		_, _, err = client.CreateGroupChannel([]string{guest.Id, user1.Id, user2.Id, user3.Id})
		require.NoError(t, err)
	})
}

func TestDeleteGroupChannel(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	user := th.BasicUser
	user2 := th.BasicUser2
	user3 := th.CreateUser()

	userIds := []string{user.Id, user2.Id, user3.Id}

	th.TestForAllClients(t, func(t *testing.T, client *model.Client4) {
		rgc, resp, err := th.Client.CreateGroupChannel(userIds)
		require.NoError(t, err)
		CheckCreatedStatus(t, resp)
		require.NotNil(t, rgc, "should have created a group channel")
		_, err = client.DeleteChannel(rgc.Id)
		CheckErrorID(t, err, "api.channel.delete_channel.type.invalid")
	})

}

func TestGetChannel(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client

	channel, _, err := client.GetChannel(th.BasicChannel.Id, "")
	require.NoError(t, err)
	require.Equal(t, th.BasicChannel.Id, channel.Id, "ids did not match")

	client.RemoveUserFromChannel(th.BasicChannel.Id, th.BasicUser.Id)
	_, _, err = client.GetChannel(th.BasicChannel.Id, "")
	require.NoError(t, err)

	channel, _, err = client.GetChannel(th.BasicPrivateChannel.Id, "")
	require.NoError(t, err)
	require.Equal(t, th.BasicPrivateChannel.Id, channel.Id, "ids did not match")

	client.RemoveUserFromChannel(th.BasicPrivateChannel.Id, th.BasicUser.Id)
	_, resp, err := client.GetChannel(th.BasicPrivateChannel.Id, "")
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	_, resp, err = client.GetChannel(model.NewId(), "")
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	client.Logout()
	_, resp, err = client.GetChannel(th.BasicChannel.Id, "")
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	user := th.CreateUser()
	client.Login(user.Email, user.Password)
	_, resp, err = client.GetChannel(th.BasicChannel.Id, "")
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
		_, _, err = client.GetChannel(th.BasicChannel.Id, "")
		require.NoError(t, err)

		_, _, err = client.GetChannel(th.BasicPrivateChannel.Id, "")
		require.NoError(t, err)

		_, resp, err = client.GetChannel(th.BasicUser.Id, "")
		require.Error(t, err)
		CheckNotFoundStatus(t, resp)
	})
}

func TestGetDeletedChannelsForTeam(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()

	client := th.Client
	team := th.BasicTeam

	th.LoginTeamAdmin()

	channels, _, err := client.GetDeletedChannelsForTeam(team.Id, 0, 100, "")
	require.NoError(t, err)
	numInitialChannelsForTeam := len(channels)

	// create and delete public channel
	publicChannel1 := th.CreatePublicChannel()
	client.DeleteChannel(publicChannel1.Id)

	th.TestForAllClients(t, func(t *testing.T, client *model.Client4) {
		channels, _, err = client.GetDeletedChannelsForTeam(team.Id, 0, 100, "")
		require.NoError(t, err)
		require.Len(t, channels, numInitialChannelsForTeam+1, "should be 1 deleted channel")
	})

	publicChannel2 := th.CreatePublicChannel()
	client.DeleteChannel(publicChannel2.Id)

	th.TestForAllClients(t, func(t *testing.T, client *model.Client4) {
		channels, _, err = client.GetDeletedChannelsForTeam(team.Id, 0, 100, "")
		require.NoError(t, err)
		require.Len(t, channels, numInitialChannelsForTeam+2, "should be 2 deleted channels")
	})

	th.LoginBasic()

	privateChannel1 := th.CreatePrivateChannel()
	client.DeleteChannel(privateChannel1.Id)

	channels, _, err = client.GetDeletedChannelsForTeam(team.Id, 0, 100, "")
	require.NoError(t, err)
	require.Len(t, channels, numInitialChannelsForTeam+3)

	// Login as different user and create private channel
	th.LoginBasic2()
	privateChannel2 := th.CreatePrivateChannel()
	client.DeleteChannel(privateChannel2.Id)

	// Log back in as first user
	th.LoginBasic()

	channels, _, err = client.GetDeletedChannelsForTeam(team.Id, 0, 100, "")
	require.NoError(t, err)
	require.Len(t, channels, numInitialChannelsForTeam+3)

	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
		channels, _, err = client.GetDeletedChannelsForTeam(team.Id, 0, 100, "")
		require.NoError(t, err)
		require.Len(t, channels, numInitialChannelsForTeam+2)
	})

	channels, _, err = client.GetDeletedChannelsForTeam(team.Id, 0, 1, "")
	require.NoError(t, err)
	require.Len(t, channels, 1, "should be one channel per page")

	channels, _, err = client.GetDeletedChannelsForTeam(team.Id, 1, 1, "")
	require.NoError(t, err)
	require.Len(t, channels, 1, "should be one channel per page")
}

func TestGetPrivateChannelsForTeam(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	team := th.BasicTeam

	// normal user
	_, resp, err := th.Client.GetPrivateChannelsForTeam(team.Id, 0, 100, "")
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	th.TestForSystemAdminAndLocal(t, func(t *testing.T, c *model.Client4) {
		channels, _, err := c.GetPrivateChannelsForTeam(team.Id, 0, 100, "")
		require.NoError(t, err)
		// th.BasicPrivateChannel and th.BasicPrivateChannel2
		require.Len(t, channels, 2, "wrong number of private channels")
		for _, c := range channels {
			// check all channels included are private
			require.Equal(t, model.ChannelTypePrivate, c.Type, "should include private channels only")
		}

		channels, _, err = c.GetPrivateChannelsForTeam(team.Id, 0, 1, "")
		require.NoError(t, err)
		require.Len(t, channels, 1, "should be one channel per page")

		channels, _, err = c.GetPrivateChannelsForTeam(team.Id, 1, 1, "")
		require.NoError(t, err)
		require.Len(t, channels, 1, "should be one channel per page")

		channels, _, err = c.GetPrivateChannelsForTeam(team.Id, 10000, 100, "")
		require.NoError(t, err)
		require.Empty(t, channels, "should be no channel")

		_, resp, err = c.GetPrivateChannelsForTeam("junk", 0, 100, "")
		require.Error(t, err)
		CheckBadRequestStatus(t, resp)
	})
}

func TestGetPublicChannelsForTeam(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client
	team := th.BasicTeam
	publicChannel1 := th.BasicChannel
	publicChannel2 := th.BasicChannel2

	channels, _, err := client.GetPublicChannelsForTeam(team.Id, 0, 100, "")
	require.NoError(t, err)
	require.Len(t, channels, 4, "wrong path")

	for i, c := range channels {
		// check all channels included are open
		require.Equal(t, model.ChannelTypeOpen, c.Type, "should include open channel only")

		// only check the created 2 public channels
		require.False(t, i < 2 && !(c.DisplayName == publicChannel1.DisplayName || c.DisplayName == publicChannel2.DisplayName), "should match public channel display name")
	}

	privateChannel := th.CreatePrivateChannel()
	channels, _, err = client.GetPublicChannelsForTeam(team.Id, 0, 100, "")
	require.NoError(t, err)
	require.Len(t, channels, 4, "incorrect length of team public channels")

	for _, c := range channels {
		require.Equal(t, model.ChannelTypeOpen, c.Type, "should not include private channel")
		require.NotEqual(t, privateChannel.DisplayName, c.DisplayName, "should not match private channel display name")
	}

	channels, _, err = client.GetPublicChannelsForTeam(team.Id, 0, 1, "")
	require.NoError(t, err)
	require.Len(t, channels, 1, "should be one channel per page")

	channels, _, err = client.GetPublicChannelsForTeam(team.Id, 1, 1, "")
	require.NoError(t, err)
	require.Len(t, channels, 1, "should be one channel per page")

	channels, _, err = client.GetPublicChannelsForTeam(team.Id, 10000, 100, "")
	require.NoError(t, err)
	require.Empty(t, channels, "should be no channel")

	_, resp, err := client.GetPublicChannelsForTeam("junk", 0, 100, "")
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	_, resp, err = client.GetPublicChannelsForTeam(model.NewId(), 0, 100, "")
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	client.Logout()
	_, resp, err = client.GetPublicChannelsForTeam(team.Id, 0, 100, "")
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	user := th.CreateUser()
	client.Login(user.Email, user.Password)
	_, resp, err = client.GetPublicChannelsForTeam(team.Id, 0, 100, "")
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
		_, _, err = client.GetPublicChannelsForTeam(team.Id, 0, 100, "")
		require.NoError(t, err)
	})
}

func TestGetPublicChannelsByIdsForTeam(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client
	teamId := th.BasicTeam.Id
	input := []string{th.BasicChannel.Id}
	output := []string{th.BasicChannel.DisplayName}

	channels, _, err := client.GetPublicChannelsByIdsForTeam(teamId, input)
	require.NoError(t, err)
	require.Len(t, channels, 1, "should return 1 channel")
	require.Equal(t, output[0], channels[0].DisplayName, "missing channel")

	input = append(input, GenerateTestId())
	input = append(input, th.BasicChannel2.Id)
	input = append(input, th.BasicPrivateChannel.Id)
	output = append(output, th.BasicChannel2.DisplayName)
	sort.Strings(output)

	channels, _, err = client.GetPublicChannelsByIdsForTeam(teamId, input)
	require.NoError(t, err)
	require.Len(t, channels, 2, "should return 2 channels")

	for i, c := range channels {
		require.Equal(t, output[i], c.DisplayName, "missing channel")
	}

	_, resp, err := client.GetPublicChannelsByIdsForTeam(GenerateTestId(), input)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	_, resp, err = client.GetPublicChannelsByIdsForTeam(teamId, []string{})
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	_, resp, err = client.GetPublicChannelsByIdsForTeam(teamId, []string{"junk"})
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	_, resp, err = client.GetPublicChannelsByIdsForTeam(teamId, []string{GenerateTestId()})
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	_, resp, err = client.GetPublicChannelsByIdsForTeam(teamId, []string{th.BasicPrivateChannel.Id})
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	client.Logout()

	_, resp, err = client.GetPublicChannelsByIdsForTeam(teamId, input)
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	_, _, err = th.SystemAdminClient.GetPublicChannelsByIdsForTeam(teamId, input)
	require.NoError(t, err)
}

func TestGetChannelsForTeamForUser(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client

	t.Run("get channels for the team for user", func(t *testing.T) {
		channels, resp, err := client.GetChannelsForTeamForUser(th.BasicTeam.Id, th.BasicUser.Id, false, "")
		require.NoError(t, err)

		found := make([]bool, 3)
		for _, c := range channels {
			if c.Id == th.BasicChannel.Id {
				found[0] = true
			} else if c.Id == th.BasicChannel2.Id {
				found[1] = true
			} else if c.Id == th.BasicPrivateChannel.Id {
				found[2] = true
			}

			require.True(t, c.TeamId == "" || c.TeamId == th.BasicTeam.Id)
		}

		for _, f := range found {
			require.True(t, f, "missing a channel")
		}

		channels, resp, _ = client.GetChannelsForTeamForUser(th.BasicTeam.Id, th.BasicUser.Id, false, resp.Etag)
		CheckEtag(t, channels, resp)

		_, resp, err = client.GetChannelsForTeamForUser(th.BasicTeam.Id, "junk", false, "")
		require.Error(t, err)
		CheckBadRequestStatus(t, resp)

		_, resp, err = client.GetChannelsForTeamForUser("junk", th.BasicUser.Id, false, "")
		require.Error(t, err)
		CheckBadRequestStatus(t, resp)

		_, resp, err = client.GetChannelsForTeamForUser(th.BasicTeam.Id, th.BasicUser2.Id, false, "")
		require.Error(t, err)
		CheckForbiddenStatus(t, resp)

		_, resp, err = client.GetChannelsForTeamForUser(model.NewId(), th.BasicUser.Id, false, "")
		require.Error(t, err)
		CheckForbiddenStatus(t, resp)

		_, _, err = th.SystemAdminClient.GetChannelsForTeamForUser(th.BasicTeam.Id, th.BasicUser.Id, false, "")
		require.NoError(t, err)
	})

	t.Run("deleted channel could be retrieved using the proper flag", func(t *testing.T) {
		testChannel := &model.Channel{
			DisplayName: "dn_" + model.NewId(),
			Name:        GenerateTestChannelName(),
			Type:        model.ChannelTypeOpen,
			TeamId:      th.BasicTeam.Id,
			CreatorId:   th.BasicUser.Id,
		}
		th.App.CreateChannel(th.Context, testChannel, true)
		defer th.App.PermanentDeleteChannel(th.Context, testChannel)
		channels, _, err := client.GetChannelsForTeamForUser(th.BasicTeam.Id, th.BasicUser.Id, false, "")
		require.NoError(t, err)
		assert.Equal(t, 6, len(channels))
		th.App.DeleteChannel(th.Context, testChannel, th.BasicUser.Id)
		channels, _, err = client.GetChannelsForTeamForUser(th.BasicTeam.Id, th.BasicUser.Id, false, "")
		require.NoError(t, err)
		assert.Equal(t, 5, len(channels))

		// Should return all channels including basicDeleted.
		channels, _, err = client.GetChannelsForTeamForUser(th.BasicTeam.Id, th.BasicUser.Id, true, "")
		require.NoError(t, err)
		assert.Equal(t, 7, len(channels))

		// Should stil return all channels including basicDeleted.
		now := time.Now().Add(-time.Minute).Unix() * 1000
		client.GetChannelsForTeamAndUserWithLastDeleteAt(th.BasicTeam.Id, th.BasicUser.Id,
			true, int(now), "")
		assert.Equal(t, 7, len(channels))
	})
}

func TestGetChannelsForUser(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()

	client := th.Client

	// Adding another team with more channels (public and private)
	myTeam := th.CreateTeam()
	ch1 := th.CreateChannelWithClientAndTeam(client, model.ChannelTypeOpen, myTeam.Id)
	ch2 := th.CreateChannelWithClientAndTeam(client, model.ChannelTypePrivate, myTeam.Id)
	th.LinkUserToTeam(th.BasicUser, myTeam)
	th.App.AddUserToChannel(th.Context, th.BasicUser, ch1, false)
	th.App.AddUserToChannel(th.Context, th.BasicUser, ch2, false)

	channels, _, err := client.GetChannelsForUserWithLastDeleteAt(th.BasicUser.Id, 0)
	require.NoError(t, err)

	numPrivate := 0
	numPublic := 0
	numOffTopic := 0
	numTownSquare := 0
	for _, ch := range channels {
		if ch.Type == model.ChannelTypeOpen {
			numPublic++
		} else if ch.Type == model.ChannelTypePrivate {
			numPrivate++
		}

		if ch.DisplayName == "Off-Topic" {
			numOffTopic++
		} else if ch.DisplayName == "Town Square" {
			numTownSquare++
		}
	}

	assert.Len(t, channels, 9)
	assert.Equal(t, 2, numPrivate)
	assert.Equal(t, 7, numPublic)
	assert.Equal(t, 2, numOffTopic)
	assert.Equal(t, 2, numTownSquare)

	// Creating some more channels to be exactly 100 to test page size boundaries.
	for i := 0; i < 91; i++ {
		ch1 = th.CreateChannelWithClientAndTeam(client, model.ChannelTypeOpen, myTeam.Id)
		th.App.AddUserToChannel(th.Context, th.BasicUser, ch1, false)
	}

	channels, _, err = client.GetChannelsForUserWithLastDeleteAt(th.BasicUser.Id, 0)
	require.NoError(t, err)
	assert.Len(t, channels, 100)
}

func TestGetAllChannels(t *testing.T) {
	th := Setup(t).InitBasic()
	th.LoginSystemManager()
	defer th.TearDown()
	client := th.Client

	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
		channels, _, err := client.GetAllChannels(0, 20, "")
		require.NoError(t, err)

		// At least, all the not-deleted channels created during the InitBasic
		require.True(t, len(channels) >= 3)
		for _, c := range channels {
			require.NotEqual(t, c.TeamId, "")
		}

		channels, _, err = client.GetAllChannels(0, 10, "")
		require.NoError(t, err)
		require.True(t, len(channels) >= 3)

		channels, _, err = client.GetAllChannels(1, 1, "")
		require.NoError(t, err)
		require.Len(t, channels, 1)

		channels, _, err = client.GetAllChannels(10000, 10000, "")
		require.NoError(t, err)
		require.Empty(t, channels)

		channels, _, err = client.GetAllChannels(0, 10000, "")
		require.NoError(t, err)
		beforeCount := len(channels)

		firstChannel := channels[0].Channel

		_, err = client.DeleteChannel(firstChannel.Id)
		require.NoError(t, err)

		channels, _, err = client.GetAllChannels(0, 10000, "")
		var ids []string
		for _, item := range channels {
			ids = append(ids, item.Channel.Id)
		}
		require.NoError(t, err)
		require.Len(t, channels, beforeCount-1)
		require.NotContains(t, ids, firstChannel.Id)

		channels, _, err = client.GetAllChannelsIncludeDeleted(0, 10000, "")
		ids = []string{}
		for _, item := range channels {
			ids = append(ids, item.Channel.Id)
		}
		require.NoError(t, err)
		require.True(t, len(channels) > beforeCount)
		require.Contains(t, ids, firstChannel.Id)
	})

	_, resp, err := client.GetAllChannels(0, 20, "")
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	sysManagerChannels, resp, err := th.SystemManagerClient.GetAllChannels(0, 10000, "")
	require.NoError(t, err)
	CheckOKStatus(t, resp)
	policyChannel := (sysManagerChannels)[0]
	policy, err := th.App.Srv().Store().RetentionPolicy().Save(&model.RetentionPolicyWithTeamAndChannelIDs{
		RetentionPolicy: model.RetentionPolicy{
			DisplayName:      "Policy 1",
			PostDurationDays: model.NewInt64(30),
		},
		ChannelIDs: []string{policyChannel.Id},
	})
	require.NoError(t, err)

	t.Run("exclude policy constrained", func(t *testing.T) {
		_, resp, err := th.SystemManagerClient.GetAllChannelsExcludePolicyConstrained(0, 10000, "")
		require.Error(t, err)
		CheckForbiddenStatus(t, resp)

		channels, resp, err := th.SystemAdminClient.GetAllChannelsExcludePolicyConstrained(0, 10000, "")
		require.NoError(t, err)
		CheckOKStatus(t, resp)
		found := false
		for _, channel := range channels {
			if channel.Id == policyChannel.Id {
				found = true
				break
			}
		}
		require.False(t, found)
	})

	t.Run("does not return policy ID", func(t *testing.T) {
		channels, resp, err := th.SystemManagerClient.GetAllChannels(0, 10000, "")
		require.NoError(t, err)
		CheckOKStatus(t, resp)
		found := false
		for _, channel := range channels {
			if channel.Id == policyChannel.Id {
				found = true
				require.Nil(t, channel.PolicyID)
				break
			}
		}
		require.True(t, found)
	})

	t.Run("returns policy ID", func(t *testing.T) {
		channels, resp, err := th.SystemAdminClient.GetAllChannels(0, 10000, "")
		require.NoError(t, err)
		CheckOKStatus(t, resp)
		found := false
		for _, channel := range channels {
			if channel.Id == policyChannel.Id {
				found = true
				require.Equal(t, *channel.PolicyID, policy.ID)
				break
			}
		}
		require.True(t, found)
	})
}

func TestGetAllChannelsWithCount(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client

	channels, total, _, err := th.SystemAdminClient.GetAllChannelsWithCount(0, 20, "")
	require.NoError(t, err)

	// At least, all the not-deleted channels created during the InitBasic
	require.True(t, len(channels) >= 3)
	for _, c := range channels {
		require.NotEqual(t, c.TeamId, "")
	}
	require.Equal(t, int64(6), total)

	channels, _, _, err = th.SystemAdminClient.GetAllChannelsWithCount(0, 10, "")
	require.NoError(t, err)
	require.True(t, len(channels) >= 3)

	channels, _, _, err = th.SystemAdminClient.GetAllChannelsWithCount(1, 1, "")
	require.NoError(t, err)
	require.Len(t, channels, 1)

	channels, _, _, err = th.SystemAdminClient.GetAllChannelsWithCount(10000, 10000, "")
	require.NoError(t, err)
	require.Empty(t, channels)

	_, _, resp, err := client.GetAllChannelsWithCount(0, 20, "")
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)
}

func TestSearchChannels(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client

	search := &model.ChannelSearch{Term: th.BasicChannel.Name}

	channels, _, err := client.SearchChannels(th.BasicTeam.Id, search)
	require.NoError(t, err)

	found := false
	for _, c := range channels {
		require.Equal(t, model.ChannelTypeOpen, c.Type, "should only return public channels")

		if c.Id == th.BasicChannel.Id {
			found = true
		}
	}
	require.True(t, found, "didn't find channel")

	search.Term = th.BasicPrivateChannel.Name
	channels, _, err = client.SearchChannels(th.BasicTeam.Id, search)
	require.NoError(t, err)

	found = false
	for _, c := range channels {
		if c.Id == th.BasicPrivateChannel.Id {
			found = true
		}
	}
	require.False(t, found, "shouldn't find private channel")

	search.Term = ""
	_, _, err = client.SearchChannels(th.BasicTeam.Id, search)
	require.NoError(t, err)

	search.Term = th.BasicChannel.Name
	_, resp, err := client.SearchChannels(model.NewId(), search)
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	_, resp, err = client.SearchChannels("junk", search)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	_, _, err = th.SystemAdminClient.SearchChannels(th.BasicTeam.Id, search)
	require.NoError(t, err)

	// Check the appropriate permissions are enforced.
	defaultRolePermissions := th.SaveDefaultRolePermissions()
	defer func() {
		th.RestoreDefaultRolePermissions(defaultRolePermissions)
	}()

	// Remove list channels permission from the user
	th.RemovePermissionFromRole(model.PermissionListTeamChannels.Id, model.TeamUserRoleId)

	t.Run("Search for a BasicChannel, which the user is a member of", func(t *testing.T) {
		search.Term = th.BasicChannel.Name
		channelList, _, err := client.SearchChannels(th.BasicTeam.Id, search)
		require.NoError(t, err)

		channelNames := []string{}
		for _, c := range channelList {
			channelNames = append(channelNames, c.Name)
		}
		require.Contains(t, channelNames, th.BasicChannel.Name)
	})

	t.Run("Remove the user from BasicChannel and search again, should not be returned", func(t *testing.T) {
		th.App.RemoveUserFromChannel(th.Context, th.BasicUser.Id, th.BasicUser.Id, th.BasicChannel)

		search.Term = th.BasicChannel.Name
		channelList, _, err := client.SearchChannels(th.BasicTeam.Id, search)
		require.NoError(t, err)

		channelNames := []string{}
		for _, c := range channelList {
			channelNames = append(channelNames, c.Name)
		}
		require.NotContains(t, channelNames, th.BasicChannel.Name)
	})

	t.Run("Guests only receive autocompletion for which accounts they are a member of", func(t *testing.T) {
		th.App.Srv().SetLicense(model.NewTestLicense(""))
		defer th.App.Srv().SetLicense(nil)

		enableGuestAccounts := *th.App.Config().GuestAccountsSettings.Enable
		defer func() {
			th.App.UpdateConfig(func(cfg *model.Config) { cfg.GuestAccountsSettings.Enable = &enableGuestAccounts })
		}()
		th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = true })

		guest := th.CreateUser()
		_, appErr := th.SystemAdminClient.DemoteUserToGuest(guest.Id)
		require.NoError(t, appErr)

		_, resp, err := th.SystemAdminClient.AddTeamMember(th.BasicTeam.Id, guest.Id)
		require.NoError(t, err)
		CheckCreatedStatus(t, resp)

		_, resp, err = client.Login(guest.Username, guest.Password)
		require.NoError(t, err)
		CheckOKStatus(t, resp)

		search.Term = th.BasicChannel2.Name
		channelList, _, err := client.SearchChannels(th.BasicTeam.Id, search)
		require.NoError(t, err)

		require.Empty(t, channelList)

		_, resp, err = th.SystemAdminClient.AddChannelMember(th.BasicChannel2.Id, guest.Id)
		require.NoError(t, err)
		CheckCreatedStatus(t, resp)

		search.Term = th.BasicChannel2.Name
		channelList, _, err = client.SearchChannels(th.BasicTeam.Id, search)
		require.NoError(t, err)

		require.NotEmpty(t, channelList)
		require.Equal(t, th.BasicChannel2.Id, channelList[0].Id)
	})
}

func TestSearchArchivedChannels(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client

	search := &model.ChannelSearch{Term: th.BasicChannel.Name}

	client.DeleteChannel(th.BasicChannel.Id)

	channels, _, err := client.SearchArchivedChannels(th.BasicTeam.Id, search)
	require.NoError(t, err)

	found := false
	for _, c := range channels {
		require.Equal(t, model.ChannelTypeOpen, c.Type)

		if c.Id == th.BasicChannel.Id {
			found = true
		}
	}

	require.True(t, found)

	search.Term = th.BasicPrivateChannel.Name
	client.DeleteChannel(th.BasicPrivateChannel.Id)

	channels, _, err = client.SearchArchivedChannels(th.BasicTeam.Id, search)
	require.NoError(t, err)

	found = false
	for _, c := range channels {
		if c.Id == th.BasicPrivateChannel.Id {
			found = true
		}
	}

	require.True(t, found)

	search.Term = ""
	_, _, err = client.SearchArchivedChannels(th.BasicTeam.Id, search)
	require.NoError(t, err)

	search.Term = th.BasicDeletedChannel.Name
	_, resp, err := client.SearchArchivedChannels(model.NewId(), search)
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	_, resp, err = client.SearchArchivedChannels("junk", search)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	_, _, err = th.SystemAdminClient.SearchArchivedChannels(th.BasicTeam.Id, search)
	require.NoError(t, err)

	// Check the appropriate permissions are enforced.
	defaultRolePermissions := th.SaveDefaultRolePermissions()
	defer func() {
		th.RestoreDefaultRolePermissions(defaultRolePermissions)
	}()

	// Remove list channels permission from the user
	th.RemovePermissionFromRole(model.PermissionListTeamChannels.Id, model.TeamUserRoleId)

	t.Run("Search for a BasicDeletedChannel, which the user is a member of", func(t *testing.T) {
		search.Term = th.BasicDeletedChannel.Name
		channelList, _, err := client.SearchArchivedChannels(th.BasicTeam.Id, search)
		require.NoError(t, err)

		channelNames := []string{}
		for _, c := range channelList {
			channelNames = append(channelNames, c.Name)
		}
		require.Contains(t, channelNames, th.BasicDeletedChannel.Name)
	})

	t.Run("Remove the user from BasicDeletedChannel and search again, should still return", func(t *testing.T) {
		th.App.RemoveUserFromChannel(th.Context, th.BasicUser.Id, th.BasicUser.Id, th.BasicDeletedChannel)

		search.Term = th.BasicDeletedChannel.Name
		channelList, _, err := client.SearchArchivedChannels(th.BasicTeam.Id, search)
		require.NoError(t, err)

		channelNames := []string{}
		for _, c := range channelList {
			channelNames = append(channelNames, c.Name)
		}
		require.Contains(t, channelNames, th.BasicDeletedChannel.Name)
	})
}

func TestSearchAllChannels(t *testing.T) {
	th := Setup(t).InitBasic()
	th.LoginSystemManager()
	defer th.TearDown()
	client := th.Client

	openChannel, _, err := th.SystemAdminClient.CreateChannel(&model.Channel{
		DisplayName: "SearchAllChannels-FOOBARDISPLAYNAME",
		Name:        "whatever",
		Type:        model.ChannelTypeOpen,
		TeamId:      th.BasicTeam.Id,
	})
	require.NoError(t, err)

	privateChannel, _, err := th.SystemAdminClient.CreateChannel(&model.Channel{
		DisplayName: "SearchAllChannels-private1",
		Name:        "private1",
		Type:        model.ChannelTypePrivate,
		TeamId:      th.BasicTeam.Id,
	})
	require.NoError(t, err)

	team := th.CreateTeam()
	privateChannel2, _, err := th.SystemAdminClient.CreateChannel(&model.Channel{
		DisplayName: "dn_private2",
		Name:        "private2",
		Type:        model.ChannelTypePrivate,
		TeamId:      team.Id,
	})
	require.NoError(t, err)
	th.LinkUserToTeam(th.SystemAdminUser, team)
	th.LinkUserToTeam(th.SystemAdminUser, th.BasicTeam)

	groupConstrainedChannel, _, err := th.SystemAdminClient.CreateChannel(&model.Channel{
		DisplayName:      "SearchAllChannels-groupConstrained-1",
		Name:             "groupconstrained1",
		Type:             model.ChannelTypePrivate,
		GroupConstrained: model.NewBool(true),
		TeamId:           team.Id,
	})
	require.NoError(t, err)

	testCases := []struct {
		Description        string
		Search             *model.ChannelSearch
		ExpectedChannelIds []string
	}{
		{
			"Middle of word search",
			&model.ChannelSearch{Term: "bardisplay"},
			[]string{openChannel.Id},
		},
		{
			"Prefix search",
			&model.ChannelSearch{Term: "SearchAllChannels-foobar"},
			[]string{openChannel.Id},
		},
		{
			"Suffix search",
			&model.ChannelSearch{Term: "displayname"},
			[]string{openChannel.Id},
		},
		{
			"Name search",
			&model.ChannelSearch{Term: "what"},
			[]string{openChannel.Id},
		},
		{
			"Name suffix search",
			&model.ChannelSearch{Term: "ever"},
			[]string{openChannel.Id},
		},
		{
			"Basic channel name middle of word search",
			&model.ChannelSearch{Term: th.BasicChannel.Name[2:14]},
			[]string{th.BasicChannel.Id},
		},
		{
			"Upper case search",
			&model.ChannelSearch{Term: strings.ToUpper(th.BasicChannel.Name)},
			[]string{th.BasicChannel.Id},
		},
		{
			"Mixed case search",
			&model.ChannelSearch{Term: th.BasicChannel.Name[0:2] + strings.ToUpper(th.BasicChannel.Name[2:5]) + th.BasicChannel.Name[5:]},
			[]string{th.BasicChannel.Id},
		},
		{
			"Non mixed case search",
			&model.ChannelSearch{Term: th.BasicChannel.Name},
			[]string{th.BasicChannel.Id},
		},
		{
			"Search private channel name",
			&model.ChannelSearch{Term: th.BasicPrivateChannel.Name},
			[]string{th.BasicPrivateChannel.Id},
		},
		{
			"Search with private channel filter",
			&model.ChannelSearch{Private: true},
			[]string{th.BasicPrivateChannel.Id, privateChannel2.Id, th.BasicPrivateChannel2.Id, privateChannel.Id, groupConstrainedChannel.Id},
		},
		{
			"Search with public channel filter",
			&model.ChannelSearch{Term: "SearchAllChannels", Public: true},
			[]string{openChannel.Id},
		},
		{
			"Search with private channel filter",
			&model.ChannelSearch{Term: "SearchAllChannels", Private: true},
			[]string{privateChannel.Id, groupConstrainedChannel.Id},
		},
		{
			"Search with teamIds channel filter",
			&model.ChannelSearch{Term: "SearchAllChannels", TeamIds: []string{th.BasicTeam.Id}},
			[]string{openChannel.Id, privateChannel.Id},
		},
		{
			"Search with deleted without IncludeDeleted filter",
			&model.ChannelSearch{Term: th.BasicDeletedChannel.Name},
			[]string{},
		},
		{
			"Search with deleted IncludeDeleted filter",
			&model.ChannelSearch{Term: th.BasicDeletedChannel.Name, IncludeDeleted: true},
			[]string{th.BasicDeletedChannel.Id},
		},
		{
			"Search with deleted IncludeDeleted filter",
			&model.ChannelSearch{Term: th.BasicDeletedChannel.Name, IncludeDeleted: true},
			[]string{th.BasicDeletedChannel.Id},
		},
		{
			"Search with deleted Deleted filter and empty term",
			&model.ChannelSearch{Term: "", Deleted: true},
			[]string{th.BasicDeletedChannel.Id},
		},
		{
			"Search for group constrained",
			&model.ChannelSearch{Term: "SearchAllChannels", GroupConstrained: true},
			[]string{groupConstrainedChannel.Id},
		},
		{
			"Search for group constrained and public",
			&model.ChannelSearch{Term: "SearchAllChannels", GroupConstrained: true, Public: true},
			[]string{},
		},
		{
			"Search for exclude group constrained",
			&model.ChannelSearch{Term: "SearchAllChannels", ExcludeGroupConstrained: true},
			[]string{openChannel.Id, privateChannel.Id},
		},
	}
	for _, testCase := range testCases {
		t.Run(testCase.Description, func(t *testing.T) {
			var channels model.ChannelListWithTeamData
			channels, _, err = th.SystemAdminClient.SearchAllChannels(testCase.Search)
			require.NoError(t, err)
			assert.Equal(t, len(testCase.ExpectedChannelIds), len(channels))
			actualChannelIds := []string{}
			for _, channelWithTeamData := range channels {
				actualChannelIds = append(actualChannelIds, channelWithTeamData.Channel.Id)
			}
			assert.ElementsMatch(t, testCase.ExpectedChannelIds, actualChannelIds)
		})
	}

	userChannels, _, err := th.SystemAdminClient.SearchAllChannelsForUser("private")
	require.NoError(t, err)
	assert.Len(t, userChannels, 2)

	userChannels, _, err = th.SystemAdminClient.SearchAllChannelsForUser("FOOBARDISPLAYNAME")
	require.NoError(t, err)
	assert.Len(t, userChannels, 1)

	// Searching with no terms returns all default channels
	allChannels, _, err := th.SystemAdminClient.SearchAllChannels(&model.ChannelSearch{Term: ""})
	require.NoError(t, err)
	assert.True(t, len(allChannels) >= 3)

	_, resp, err := client.SearchAllChannels(&model.ChannelSearch{Term: ""})
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	// Choose a policy which the system manager can read
	sysManagerChannels, resp, err := th.SystemManagerClient.GetAllChannels(0, 10000, "")
	require.NoError(t, err)
	CheckOKStatus(t, resp)
	policyChannel := sysManagerChannels[0]
	policy, savePolicyErr := th.App.Srv().Store().RetentionPolicy().Save(&model.RetentionPolicyWithTeamAndChannelIDs{
		RetentionPolicy: model.RetentionPolicy{
			DisplayName:      "Policy 1",
			PostDurationDays: model.NewInt64(30),
		},
		ChannelIDs: []string{policyChannel.Id},
	})
	require.NoError(t, savePolicyErr)

	t.Run("does not return policy ID", func(t *testing.T) {
		channels, resp, err := th.SystemManagerClient.SearchAllChannels(&model.ChannelSearch{Term: policyChannel.Name})
		require.NoError(t, err)
		CheckOKStatus(t, resp)
		found := false
		for _, channel := range channels {
			if channel.Id == policyChannel.Id {
				found = true
				require.Nil(t, channel.PolicyID)
				break
			}
		}
		require.True(t, found)
	})
	t.Run("returns policy ID", func(t *testing.T) {
		channels, resp, err := th.SystemAdminClient.SearchAllChannels(&model.ChannelSearch{Term: policyChannel.Name})
		require.NoError(t, err)
		CheckOKStatus(t, resp)
		found := false
		for _, channel := range channels {
			if channel.Id == policyChannel.Id {
				found = true
				require.Equal(t, *channel.PolicyID, policy.ID)
				break
			}
		}
		require.True(t, found)
	})
}

func TestSearchAllChannelsPaged(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client

	search := &model.ChannelSearch{Term: th.BasicChannel.Name}
	search.Term = ""
	search.Page = model.NewInt(0)
	search.PerPage = model.NewInt(2)
	channelsWithCount, _, err := th.SystemAdminClient.SearchAllChannelsPaged(search)
	require.NoError(t, err)
	require.Len(t, channelsWithCount.Channels, 2)

	search.Term = th.BasicChannel.Name
	_, resp, err := client.SearchAllChannels(search)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)
}

func TestSearchGroupChannels(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client

	u1 := th.CreateUserWithClient(th.SystemAdminClient)

	// Create a group channel in which base user belongs but not sysadmin
	gc1, _, err := th.Client.CreateGroupChannel([]string{th.BasicUser.Id, th.BasicUser2.Id, u1.Id})
	require.NoError(t, err)
	defer th.Client.DeleteChannel(gc1.Id)

	gc2, _, err := th.Client.CreateGroupChannel([]string{th.BasicUser.Id, th.BasicUser2.Id, th.SystemAdminUser.Id})
	require.NoError(t, err)
	defer th.Client.DeleteChannel(gc2.Id)

	search := &model.ChannelSearch{Term: th.BasicUser2.Username}

	// sysadmin should only find gc2 as he doesn't belong to gc1
	channels, _, err := th.SystemAdminClient.SearchGroupChannels(search)
	require.NoError(t, err)

	assert.Len(t, channels, 1)
	assert.Equal(t, channels[0].Id, gc2.Id)

	// basic user should find both
	client.Login(th.BasicUser.Username, th.BasicUser.Password)
	channels, _, err = client.SearchGroupChannels(search)
	require.NoError(t, err)

	assert.Len(t, channels, 2)
	channelIds := []string{}
	for _, c := range channels {
		channelIds = append(channelIds, c.Id)
	}
	assert.ElementsMatch(t, channelIds, []string{gc1.Id, gc2.Id})

	// searching for sysadmin, it should only find gc1
	search = &model.ChannelSearch{Term: th.SystemAdminUser.Username}
	channels, _, err = client.SearchGroupChannels(search)
	require.NoError(t, err)

	assert.Len(t, channels, 1)
	assert.Equal(t, channels[0].Id, gc2.Id)

	// with an empty search, response should be empty
	search = &model.ChannelSearch{Term: ""}
	channels, _, err = client.SearchGroupChannels(search)
	require.NoError(t, err)

	assert.Empty(t, channels)

	// search unprivileged, forbidden
	th.Client.Logout()
	_, resp, err := client.SearchAllChannels(search)
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)
}

func TestDeleteChannel(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	c := th.Client
	team := th.BasicTeam
	user := th.BasicUser
	user2 := th.BasicUser2

	// successful delete of public channel
	th.TestForAllClients(t, func(t *testing.T, client *model.Client4) {
		publicChannel1 := th.CreatePublicChannel()
		_, err := client.DeleteChannel(publicChannel1.Id)
		require.NoError(t, err)

		ch, appErr := th.App.GetChannel(th.Context, publicChannel1.Id)
		require.Nilf(t, appErr, "Expected nil, Got %v", appErr)
		require.True(t, ch.DeleteAt != 0, "should have returned one with a populated DeleteAt.")

		post1 := &model.Post{ChannelId: publicChannel1.Id, Message: "a" + GenerateTestId() + "a"}
		_, resp, _ := client.CreatePost(post1)
		require.NotNil(t, resp, "expected response to not be nil")

		// successful delete of private channel
		privateChannel2 := th.CreatePrivateChannel()
		_, err = client.DeleteChannel(privateChannel2.Id)
		require.NoError(t, err)

		// successful delete of channel with multiple members
		publicChannel3 := th.CreatePublicChannel()
		th.App.AddUserToChannel(th.Context, user, publicChannel3, false)
		th.App.AddUserToChannel(th.Context, user2, publicChannel3, false)
		_, err = client.DeleteChannel(publicChannel3.Id)
		require.NoError(t, err)

		// default channel cannot be deleted.
		defaultChannel, _ := th.App.GetChannelByName(th.Context, model.DefaultChannelName, team.Id, false)
		resp, err = client.DeleteChannel(defaultChannel.Id)
		require.Error(t, err)
		CheckBadRequestStatus(t, resp)

		// check system admin can delete a channel without any appropriate team or channel membership.
		sdTeam := th.CreateTeamWithClient(c)
		sdPublicChannel := &model.Channel{
			DisplayName: "dn_" + model.NewId(),
			Name:        GenerateTestChannelName(),
			Type:        model.ChannelTypeOpen,
			TeamId:      sdTeam.Id,
		}
		sdPublicChannel, _, err = c.CreateChannel(sdPublicChannel)
		require.NoError(t, err)
		_, err = client.DeleteChannel(sdPublicChannel.Id)
		require.NoError(t, err)

		sdPrivateChannel := &model.Channel{
			DisplayName: "dn_" + model.NewId(),
			Name:        GenerateTestChannelName(),
			Type:        model.ChannelTypePrivate,
			TeamId:      sdTeam.Id,
		}
		sdPrivateChannel, _, err = c.CreateChannel(sdPrivateChannel)
		require.NoError(t, err)
		_, err = client.DeleteChannel(sdPrivateChannel.Id)
		require.NoError(t, err)
	})
	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {

		th.LoginBasic()
		publicChannel5 := th.CreatePublicChannel()
		c.Logout()

		c.Login(user.Id, user.Password)
		resp, err := c.DeleteChannel(publicChannel5.Id)
		require.Error(t, err)
		CheckUnauthorizedStatus(t, resp)

		resp, err = c.DeleteChannel("junk")
		require.Error(t, err)
		CheckUnauthorizedStatus(t, resp)

		c.Logout()
		resp, err = c.DeleteChannel(GenerateTestId())
		require.Error(t, err)
		CheckUnauthorizedStatus(t, resp)

		_, err = client.DeleteChannel(publicChannel5.Id)
		require.NoError(t, err)

	})

}

func TestDeleteChannel2(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client
	user := th.BasicUser

	// Check the appropriate permissions are enforced.
	defaultRolePermissions := th.SaveDefaultRolePermissions()
	defer func() {
		th.RestoreDefaultRolePermissions(defaultRolePermissions)
	}()

	th.AddPermissionToRole(model.PermissionDeletePublicChannel.Id, model.ChannelUserRoleId)
	th.AddPermissionToRole(model.PermissionDeletePrivateChannel.Id, model.ChannelUserRoleId)

	// channels created by SystemAdmin
	publicChannel6 := th.CreateChannelWithClient(th.SystemAdminClient, model.ChannelTypeOpen)
	privateChannel7 := th.CreateChannelWithClient(th.SystemAdminClient, model.ChannelTypePrivate)
	th.App.AddUserToChannel(th.Context, user, publicChannel6, false)
	th.App.AddUserToChannel(th.Context, user, privateChannel7, false)
	th.App.AddUserToChannel(th.Context, user, privateChannel7, false)

	// successful delete by user
	_, err := client.DeleteChannel(publicChannel6.Id)
	require.NoError(t, err)

	_, err = client.DeleteChannel(privateChannel7.Id)
	require.NoError(t, err)

	// Restrict permissions to Channel Admins
	th.RemovePermissionFromRole(model.PermissionDeletePublicChannel.Id, model.ChannelUserRoleId)
	th.RemovePermissionFromRole(model.PermissionDeletePrivateChannel.Id, model.ChannelUserRoleId)
	th.AddPermissionToRole(model.PermissionDeletePublicChannel.Id, model.ChannelAdminRoleId)
	th.AddPermissionToRole(model.PermissionDeletePrivateChannel.Id, model.ChannelAdminRoleId)

	// channels created by SystemAdmin
	publicChannel6 = th.CreateChannelWithClient(th.SystemAdminClient, model.ChannelTypeOpen)
	privateChannel7 = th.CreateChannelWithClient(th.SystemAdminClient, model.ChannelTypePrivate)
	th.App.AddUserToChannel(th.Context, user, publicChannel6, false)
	th.App.AddUserToChannel(th.Context, user, privateChannel7, false)
	th.App.AddUserToChannel(th.Context, user, privateChannel7, false)

	// cannot delete by user
	resp, err := client.DeleteChannel(publicChannel6.Id)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	resp, err = client.DeleteChannel(privateChannel7.Id)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	// successful delete by channel admin
	th.MakeUserChannelAdmin(user, publicChannel6)
	th.MakeUserChannelAdmin(user, privateChannel7)
	th.App.Srv().Store().Channel().ClearCaches()

	_, err = client.DeleteChannel(publicChannel6.Id)
	require.NoError(t, err)

	_, err = client.DeleteChannel(privateChannel7.Id)
	require.NoError(t, err)

	// Make sure team admins don't have permission to delete channels.
	th.RemovePermissionFromRole(model.PermissionDeletePublicChannel.Id, model.ChannelAdminRoleId)
	th.RemovePermissionFromRole(model.PermissionDeletePrivateChannel.Id, model.ChannelAdminRoleId)

	// last member of a public channel should have required permission to delete
	publicChannel6 = th.CreateChannelWithClient(th.Client, model.ChannelTypeOpen)
	resp, err = client.DeleteChannel(publicChannel6.Id)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	// last member of a private channel should not be able to delete it if they don't have required permissions
	privateChannel7 = th.CreateChannelWithClient(th.Client, model.ChannelTypePrivate)
	resp, err = client.DeleteChannel(privateChannel7.Id)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)
}

func TestPermanentDeleteChannel(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()

	enableAPIChannelDeletion := *th.App.Config().ServiceSettings.EnableAPIChannelDeletion
	defer func() {
		th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableAPIChannelDeletion = &enableAPIChannelDeletion })
	}()

	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableAPIChannelDeletion = false })

	publicChannel1 := th.CreatePublicChannel()
	t.Run("Permanent deletion not available through API if EnableAPIChannelDeletion is not set", func(t *testing.T) {
		resp, err := th.SystemAdminClient.PermanentDeleteChannel(publicChannel1.Id)
		require.Error(t, err)
		CheckUnauthorizedStatus(t, resp)
	})

	t.Run("Permanent deletion available through local mode even if EnableAPIChannelDeletion is not set", func(t *testing.T) {
		_, err := th.LocalClient.PermanentDeleteChannel(publicChannel1.Id)
		require.NoError(t, err)
	})

	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableAPIChannelDeletion = true })
	th.TestForSystemAdminAndLocal(t, func(t *testing.T, c *model.Client4) {
		publicChannel := th.CreatePublicChannel()
		_, err := c.PermanentDeleteChannel(publicChannel.Id)
		require.NoError(t, err)

		_, appErr := th.App.GetChannel(th.Context, publicChannel.Id)
		assert.NotNil(t, appErr)

		resp, err := c.PermanentDeleteChannel("junk")
		require.Error(t, err)
		CheckBadRequestStatus(t, resp)
	}, "Permanent deletion with EnableAPIChannelDeletion set")
}

func TestUpdateChannelPrivacy(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()

	defaultChannel, _ := th.App.GetChannelByName(th.Context, model.DefaultChannelName, th.BasicTeam.Id, false)

	type testTable []struct {
		name            string
		channel         *model.Channel
		expectedPrivacy model.ChannelType
	}

	t.Run("Should get a forbidden response if not logged in", func(t *testing.T) {
		privateChannel := th.CreatePrivateChannel()
		publicChannel := th.CreatePublicChannel()

		tt := testTable{
			{"Updating default channel should fail with forbidden status if not logged in", defaultChannel, model.ChannelTypeOpen},
			{"Updating private channel should fail with forbidden status if not logged in", privateChannel, model.ChannelTypePrivate},
			{"Updating public channel should fail with forbidden status if not logged in", publicChannel, model.ChannelTypeOpen},
		}

		for _, tc := range tt {
			t.Run(tc.name, func(t *testing.T) {
				_, resp, err := th.Client.UpdateChannelPrivacy(tc.channel.Id, tc.expectedPrivacy)
				require.Error(t, err)
				CheckForbiddenStatus(t, resp)
			})
		}
	})

	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
		privateChannel := th.CreatePrivateChannel()
		publicChannel := th.CreatePublicChannel()

		tt := testTable{
			{"Converting default channel to private should fail", defaultChannel, model.ChannelTypePrivate},
			{"Updating privacy to an invalid setting should fail", publicChannel, "invalid"},
		}

		for _, tc := range tt {
			t.Run(tc.name, func(t *testing.T) {
				_, resp, err := client.UpdateChannelPrivacy(tc.channel.Id, tc.expectedPrivacy)
				require.Error(t, err)
				CheckBadRequestStatus(t, resp)
			})
		}

		tt = testTable{
			{"Default channel should stay public", defaultChannel, model.ChannelTypeOpen},
			{"Public channel should stay public", publicChannel, model.ChannelTypeOpen},
			{"Private channel should stay private", privateChannel, model.ChannelTypePrivate},
			{"Public channel should convert to private", publicChannel, model.ChannelTypePrivate},
			{"Private channel should convert to public", privateChannel, model.ChannelTypeOpen},
		}

		for _, tc := range tt {
			t.Run(tc.name, func(t *testing.T) {
				updatedChannel, _, err := client.UpdateChannelPrivacy(tc.channel.Id, tc.expectedPrivacy)
				require.NoError(t, err)
				assert.Equal(t, tc.expectedPrivacy, updatedChannel.Type)
				updatedChannel, appErr := th.App.GetChannel(th.Context, tc.channel.Id)
				require.Nil(t, appErr)
				assert.Equal(t, tc.expectedPrivacy, updatedChannel.Type)
			})
		}
	})

	t.Run("Enforces convert channel permissions", func(t *testing.T) {
		privateChannel := th.CreatePrivateChannel()
		publicChannel := th.CreatePublicChannel()

		th.LoginTeamAdmin()

		th.RemovePermissionFromRole(model.PermissionConvertPublicChannelToPrivate.Id, model.TeamAdminRoleId)
		th.RemovePermissionFromRole(model.PermissionConvertPrivateChannelToPublic.Id, model.TeamAdminRoleId)

		_, resp, err := th.Client.UpdateChannelPrivacy(publicChannel.Id, model.ChannelTypePrivate)
		require.Error(t, err)
		CheckForbiddenStatus(t, resp)
		_, resp, err = th.Client.UpdateChannelPrivacy(privateChannel.Id, model.ChannelTypeOpen)
		require.Error(t, err)
		CheckForbiddenStatus(t, resp)

		th.AddPermissionToRole(model.PermissionConvertPublicChannelToPrivate.Id, model.TeamAdminRoleId)
		th.AddPermissionToRole(model.PermissionConvertPrivateChannelToPublic.Id, model.TeamAdminRoleId)

		_, _, err = th.Client.UpdateChannelPrivacy(privateChannel.Id, model.ChannelTypeOpen)
		require.NoError(t, err)
		_, _, err = th.Client.UpdateChannelPrivacy(publicChannel.Id, model.ChannelTypePrivate)
		require.NoError(t, err)
	})
}

func TestRestoreChannel(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()

	publicChannel1 := th.CreatePublicChannel()
	th.Client.DeleteChannel(publicChannel1.Id)

	privateChannel1 := th.CreatePrivateChannel()
	th.Client.DeleteChannel(privateChannel1.Id)

	_, resp, err := th.Client.RestoreChannel(publicChannel1.Id)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	_, resp, err = th.Client.RestoreChannel(privateChannel1.Id)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
		defer func() {
			client.DeleteChannel(publicChannel1.Id)
			client.DeleteChannel(privateChannel1.Id)
		}()

		_, resp, err = client.RestoreChannel(publicChannel1.Id)
		require.NoError(t, err)
		CheckOKStatus(t, resp)

		_, resp, err = client.RestoreChannel(privateChannel1.Id)
		require.NoError(t, err)
		CheckOKStatus(t, resp)
	})
}

func TestGetChannelByName(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client

	channel, _, err := client.GetChannelByName(th.BasicChannel.Name, th.BasicTeam.Id, "")
	require.NoError(t, err)
	require.Equal(t, th.BasicChannel.Name, channel.Name, "names did not match")

	channel, _, err = client.GetChannelByName(th.BasicPrivateChannel.Name, th.BasicTeam.Id, "")
	require.NoError(t, err)
	require.Equal(t, th.BasicPrivateChannel.Name, channel.Name, "names did not match")

	_, _, err = client.GetChannelByName(strings.ToUpper(th.BasicPrivateChannel.Name), th.BasicTeam.Id, "")
	require.NoError(t, err)

	_, resp, err := client.GetChannelByName(th.BasicDeletedChannel.Name, th.BasicTeam.Id, "")
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	channel, _, err = client.GetChannelByNameIncludeDeleted(th.BasicDeletedChannel.Name, th.BasicTeam.Id, "")
	require.NoError(t, err)
	require.Equal(t, th.BasicDeletedChannel.Name, channel.Name, "names did not match")

	client.RemoveUserFromChannel(th.BasicChannel.Id, th.BasicUser.Id)
	_, _, err = client.GetChannelByName(th.BasicChannel.Name, th.BasicTeam.Id, "")
	require.NoError(t, err)

	client.RemoveUserFromChannel(th.BasicPrivateChannel.Id, th.BasicUser.Id)
	_, resp, err = client.GetChannelByName(th.BasicPrivateChannel.Name, th.BasicTeam.Id, "")
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	_, resp, err = client.GetChannelByName(GenerateTestChannelName(), th.BasicTeam.Id, "")
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	_, resp, err = client.GetChannelByName(GenerateTestChannelName(), "junk", "")
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	client.Logout()
	_, resp, err = client.GetChannelByName(th.BasicChannel.Name, th.BasicTeam.Id, "")
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	user := th.CreateUser()
	client.Login(user.Email, user.Password)
	_, resp, err = client.GetChannelByName(th.BasicChannel.Name, th.BasicTeam.Id, "")
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
		_, _, err = client.GetChannelByName(th.BasicChannel.Name, th.BasicTeam.Id, "")
		require.NoError(t, err)
	})
}

func TestGetChannelByNameForTeamName(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client

	channel, _, err := th.SystemAdminClient.GetChannelByNameForTeamName(th.BasicChannel.Name, th.BasicTeam.Name, "")
	require.NoError(t, err)
	require.Equal(t, th.BasicChannel.Name, channel.Name, "names did not match")

	_, _, err = client.GetChannelByNameForTeamName(th.BasicChannel.Name, th.BasicTeam.Name, "")
	require.NoError(t, err)
	require.Equal(t, th.BasicChannel.Name, channel.Name, "names did not match")

	channel, _, err = client.GetChannelByNameForTeamName(th.BasicPrivateChannel.Name, th.BasicTeam.Name, "")
	require.NoError(t, err)
	require.Equal(t, th.BasicPrivateChannel.Name, channel.Name, "names did not match")

	_, resp, err := client.GetChannelByNameForTeamName(th.BasicDeletedChannel.Name, th.BasicTeam.Name, "")
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	channel, _, err = client.GetChannelByNameForTeamNameIncludeDeleted(th.BasicDeletedChannel.Name, th.BasicTeam.Name, "")
	require.NoError(t, err)
	require.Equal(t, th.BasicDeletedChannel.Name, channel.Name, "names did not match")

	client.RemoveUserFromChannel(th.BasicChannel.Id, th.BasicUser.Id)
	_, _, err = client.GetChannelByNameForTeamName(th.BasicChannel.Name, th.BasicTeam.Name, "")
	require.NoError(t, err)

	client.RemoveUserFromChannel(th.BasicPrivateChannel.Id, th.BasicUser.Id)
	_, resp, err = client.GetChannelByNameForTeamName(th.BasicPrivateChannel.Name, th.BasicTeam.Name, "")
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	_, resp, err = client.GetChannelByNameForTeamName(th.BasicChannel.Name, model.NewRandomString(15), "")
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	_, resp, err = client.GetChannelByNameForTeamName(GenerateTestChannelName(), th.BasicTeam.Name, "")
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	client.Logout()
	_, resp, err = client.GetChannelByNameForTeamName(th.BasicChannel.Name, th.BasicTeam.Name, "")
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	user := th.CreateUser()
	client.Login(user.Email, user.Password)
	_, resp, err = client.GetChannelByNameForTeamName(th.BasicChannel.Name, th.BasicTeam.Name, "")
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)
}

func TestGetChannelMembers(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	th.TestForAllClients(t, func(t *testing.T, client *model.Client4) {
		members, _, err := client.GetChannelMembers(th.BasicChannel.Id, 0, 60, "")
		require.NoError(t, err)
		require.Len(t, members, 3, "should only be 3 users in channel")

		members, _, err = client.GetChannelMembers(th.BasicChannel.Id, 0, 2, "")
		require.NoError(t, err)
		require.Len(t, members, 2, "should only be 2 users")

		members, _, err = client.GetChannelMembers(th.BasicChannel.Id, 1, 1, "")
		require.NoError(t, err)
		require.Len(t, members, 1, "should only be 1 user")

		members, _, err = client.GetChannelMembers(th.BasicChannel.Id, 1000, 100000, "")
		require.NoError(t, err)
		require.Empty(t, members, "should be 0 users")

		_, resp, err := client.GetChannelMembers("junk", 0, 60, "")
		require.Error(t, err)
		CheckBadRequestStatus(t, resp)

		_, resp, err = client.GetChannelMembers("", 0, 60, "")
		require.Error(t, err)
		CheckBadRequestStatus(t, resp)

		_, _, err = client.GetChannelMembers(th.BasicChannel.Id, 0, 60, "")
		require.NoError(t, err)
	})

	_, resp, err := th.Client.GetChannelMembers(model.NewId(), 0, 60, "")
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	th.Client.Logout()
	_, resp, err = th.Client.GetChannelMembers(th.BasicChannel.Id, 0, 60, "")
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	user := th.CreateUser()
	th.Client.Login(user.Email, user.Password)
	_, resp, err = th.Client.GetChannelMembers(th.BasicChannel.Id, 0, 60, "")
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)
}

func TestGetChannelMembersByIds(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client

	cm, _, err := client.GetChannelMembersByIds(th.BasicChannel.Id, []string{th.BasicUser.Id})
	require.NoError(t, err)
	require.Equal(t, th.BasicUser.Id, cm[0].UserId, "returned wrong user")

	_, resp, err := client.GetChannelMembersByIds(th.BasicChannel.Id, []string{})
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	cm1, _, err := client.GetChannelMembersByIds(th.BasicChannel.Id, []string{"junk"})
	require.NoError(t, err)
	require.Empty(t, cm1, "no users should be returned")

	cm1, _, err = client.GetChannelMembersByIds(th.BasicChannel.Id, []string{"junk", th.BasicUser.Id})
	require.NoError(t, err)
	require.Len(t, cm1, 1, "1 member should be returned")

	cm1, _, err = client.GetChannelMembersByIds(th.BasicChannel.Id, []string{th.BasicUser2.Id, th.BasicUser.Id})
	require.NoError(t, err)
	require.Len(t, cm1, 2, "2 members should be returned")

	_, resp, err = client.GetChannelMembersByIds("junk", []string{th.BasicUser.Id})
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	_, resp, err = client.GetChannelMembersByIds(model.NewId(), []string{th.BasicUser.Id})
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	client.Logout()
	_, resp, err = client.GetChannelMembersByIds(th.BasicChannel.Id, []string{th.BasicUser.Id})
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	_, _, err = th.SystemAdminClient.GetChannelMembersByIds(th.BasicChannel.Id, []string{th.BasicUser2.Id, th.BasicUser.Id})
	require.NoError(t, err)
}

func TestGetChannelMember(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	c := th.Client
	th.TestForAllClients(t, func(t *testing.T, client *model.Client4) {
		member, _, err := client.GetChannelMember(th.BasicChannel.Id, th.BasicUser.Id, "")
		require.NoError(t, err)
		require.Equal(t, th.BasicChannel.Id, member.ChannelId, "wrong channel id")
		require.Equal(t, th.BasicUser.Id, member.UserId, "wrong user id")

		_, resp, err := client.GetChannelMember("", th.BasicUser.Id, "")
		require.Error(t, err)
		CheckNotFoundStatus(t, resp)

		_, resp, err = client.GetChannelMember("junk", th.BasicUser.Id, "")
		require.Error(t, err)
		CheckBadRequestStatus(t, resp)
		_, resp, err = client.GetChannelMember(th.BasicChannel.Id, "", "")
		require.Error(t, err)
		CheckNotFoundStatus(t, resp)

		_, resp, err = client.GetChannelMember(th.BasicChannel.Id, "junk", "")
		require.Error(t, err)
		CheckBadRequestStatus(t, resp)

		_, resp, err = client.GetChannelMember(th.BasicChannel.Id, model.NewId(), "")
		require.Error(t, err)
		CheckNotFoundStatus(t, resp)

		_, _, err = client.GetChannelMember(th.BasicChannel.Id, th.BasicUser.Id, "")
		require.NoError(t, err)
	})

	_, resp, err := c.GetChannelMember(model.NewId(), th.BasicUser.Id, "")
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	c.Logout()
	_, resp, err = c.GetChannelMember(th.BasicChannel.Id, th.BasicUser.Id, "")
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	user := th.CreateUser()
	c.Login(user.Email, user.Password)
	_, resp, err = c.GetChannelMember(th.BasicChannel.Id, th.BasicUser.Id, "")
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)
}

func TestGetChannelMembersForUser(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client

	members, _, err := client.GetChannelMembersForUser(th.BasicUser.Id, th.BasicTeam.Id, "")
	require.NoError(t, err)
	require.Len(t, members, 6, "should have 6 members on team")

	_, resp, err := client.GetChannelMembersForUser("", th.BasicTeam.Id, "")
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	_, resp, err = client.GetChannelMembersForUser("junk", th.BasicTeam.Id, "")
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	_, resp, err = client.GetChannelMembersForUser(model.NewId(), th.BasicTeam.Id, "")
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	_, resp, err = client.GetChannelMembersForUser(th.BasicUser.Id, "", "")
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	_, resp, err = client.GetChannelMembersForUser(th.BasicUser.Id, "junk", "")
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	_, resp, err = client.GetChannelMembersForUser(th.BasicUser.Id, model.NewId(), "")
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	client.Logout()
	_, resp, err = client.GetChannelMembersForUser(th.BasicUser.Id, th.BasicTeam.Id, "")
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	user := th.CreateUser()
	client.Login(user.Email, user.Password)
	_, resp, err = client.GetChannelMembersForUser(th.BasicUser.Id, th.BasicTeam.Id, "")
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	_, _, err = th.SystemAdminClient.GetChannelMembersForUser(th.BasicUser.Id, th.BasicTeam.Id, "")
	require.NoError(t, err)
}

func TestViewChannel(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client

	view := &model.ChannelView{
		ChannelId: th.BasicChannel.Id,
	}

	viewResp, _, err := client.ViewChannel(th.BasicUser.Id, view)
	require.NoError(t, err)
	require.Equal(t, "OK", viewResp.Status, "should have passed")

	channel, _ := th.App.GetChannel(th.Context, th.BasicChannel.Id)

	require.Equal(t, channel.LastPostAt, viewResp.LastViewedAtTimes[channel.Id], "LastPostAt does not match returned LastViewedAt time")

	view.PrevChannelId = th.BasicChannel.Id
	_, _, err = client.ViewChannel(th.BasicUser.Id, view)
	require.NoError(t, err)

	view.PrevChannelId = ""
	_, _, err = client.ViewChannel(th.BasicUser.Id, view)
	require.NoError(t, err)

	view.PrevChannelId = "junk"
	_, resp, err := client.ViewChannel(th.BasicUser.Id, view)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	// All blank is OK we use it for clicking off of the browser.
	view.PrevChannelId = ""
	view.ChannelId = ""
	_, _, err = client.ViewChannel(th.BasicUser.Id, view)
	require.NoError(t, err)

	view.PrevChannelId = ""
	view.ChannelId = "junk"
	_, resp, err = client.ViewChannel(th.BasicUser.Id, view)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	view.ChannelId = "correctlysizedjunkdddfdfdf"
	_, resp, err = client.ViewChannel(th.BasicUser.Id, view)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)
	view.ChannelId = th.BasicChannel.Id

	member, _, err := client.GetChannelMember(th.BasicChannel.Id, th.BasicUser.Id, "")
	require.NoError(t, err)
	channel, _, err = client.GetChannel(th.BasicChannel.Id, "")
	require.NoError(t, err)
	require.Equal(t, channel.TotalMsgCount, member.MsgCount, "should match message counts")
	require.Equal(t, int64(0), member.MentionCount, "should have no mentions")
	require.Equal(t, int64(0), member.MentionCountRoot, "should have no mentions")

	_, resp, err = client.ViewChannel("junk", view)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	_, resp, err = client.ViewChannel(th.BasicUser2.Id, view)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	r, err := client.DoAPIPost(fmt.Sprintf("/channels/members/%v/view", th.BasicUser.Id), "garbage")
	require.Error(t, err)
	require.Equal(t, http.StatusBadRequest, r.StatusCode)

	client.Logout()
	_, resp, err = client.ViewChannel(th.BasicUser.Id, view)
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	_, _, err = th.SystemAdminClient.ViewChannel(th.BasicUser.Id, view)
	require.NoError(t, err)
}

func TestGetChannelUnread(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client
	user := th.BasicUser
	channel := th.BasicChannel

	channelUnread, _, err := client.GetChannelUnread(channel.Id, user.Id)
	require.NoError(t, err)
	require.Equal(t, th.BasicTeam.Id, channelUnread.TeamId, "wrong team id returned for a regular user call")
	require.Equal(t, channel.Id, channelUnread.ChannelId, "wrong team id returned for a regular user call")

	_, resp, err := client.GetChannelUnread("junk", user.Id)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	_, resp, err = client.GetChannelUnread(channel.Id, "junk")
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	_, resp, err = client.GetChannelUnread(channel.Id, model.NewId())
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	_, resp, err = client.GetChannelUnread(model.NewId(), user.Id)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	newUser := th.CreateUser()
	client.Login(newUser.Email, newUser.Password)
	_, resp, err = client.GetChannelUnread(th.BasicChannel.Id, user.Id)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	client.Logout()

	_, _, err = th.SystemAdminClient.GetChannelUnread(channel.Id, user.Id)
	require.NoError(t, err)

	_, resp, err = th.SystemAdminClient.GetChannelUnread(model.NewId(), user.Id)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	_, resp, err = th.SystemAdminClient.GetChannelUnread(channel.Id, model.NewId())
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)
}

func TestGetChannelStats(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client
	channel := th.CreatePrivateChannel()

	stats, _, err := client.GetChannelStats(channel.Id, "", false)
	require.NoError(t, err)

	require.Equal(t, channel.Id, stats.ChannelId, "couldn't get extra info")
	require.Equal(t, int64(1), stats.MemberCount, "got incorrect member count")
	require.Equal(t, int64(0), stats.PinnedPostCount, "got incorrect pinned post count")
	require.Equal(t, int64(0), stats.FilesCount, "got incorrect file count")

	th.CreatePinnedPostWithClient(th.Client, channel)
	stats, _, err = client.GetChannelStats(channel.Id, "", false)
	require.NoError(t, err)
	require.Equal(t, int64(1), stats.PinnedPostCount, "should have returned 1 pinned post count")

	// create a post with a file
	sent, err := testutils.ReadTestFile("test.png")
	require.NoError(t, err)
	fileResp, _, err := client.UploadFile(sent, channel.Id, "test.png")
	require.NoError(t, err)
	th.CreatePostInChannelWithFiles(channel, fileResp.FileInfos...)
	// make sure the file count channel stats is updated
	stats, _, err = client.GetChannelStats(channel.Id, "", false)
	require.NoError(t, err)
	require.Equal(t, int64(1), stats.FilesCount, "should have returned 1 file count")

	// exclude file counts
	stats, _, err = client.GetChannelStats(channel.Id, "", true)
	require.NoError(t, err)
	require.Equal(t, int64(-1), stats.FilesCount, "should have returned -1 file count for exclude_files_count=true")

	_, resp, err := client.GetChannelStats("junk", "", false)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	_, resp, err = client.GetChannelStats(model.NewId(), "", false)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	client.Logout()
	_, resp, err = client.GetChannelStats(channel.Id, "", false)
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	th.LoginBasic2()

	_, resp, err = client.GetChannelStats(channel.Id, "", false)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	_, _, err = th.SystemAdminClient.GetChannelStats(channel.Id, "", false)
	require.NoError(t, err)
}

func TestGetPinnedPosts(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client
	channel := th.BasicChannel

	posts, _, err := client.GetPinnedPosts(channel.Id, "")
	require.NoError(t, err)
	require.Empty(t, posts.Posts, "should not have gotten a pinned post")

	pinnedPost := th.CreatePinnedPost()
	posts, resp, err := client.GetPinnedPosts(channel.Id, "")
	require.NoError(t, err)
	require.Len(t, posts.Posts, 1, "should have returned 1 pinned post")
	require.Contains(t, posts.Posts, pinnedPost.Id, "missing pinned post")

	posts, resp, _ = client.GetPinnedPosts(channel.Id, resp.Etag)
	CheckEtag(t, posts, resp)

	_, resp, err = client.GetPinnedPosts(GenerateTestId(), "")
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	_, resp, err = client.GetPinnedPosts("junk", "")
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	client.Logout()
	_, resp, err = client.GetPinnedPosts(channel.Id, "")
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	_, _, err = th.SystemAdminClient.GetPinnedPosts(channel.Id, "")
	require.NoError(t, err)
}

func TestUpdateChannelRoles(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client

	const ChannelAdmin = "channel_user channel_admin"
	const ChannelMember = "channel_user"

	// User 1 creates a channel, making them channel admin by default.
	channel := th.CreatePublicChannel()

	// Adds User 2 to the channel, making them a channel member by default.
	th.App.AddUserToChannel(th.Context, th.BasicUser2, channel, false)

	// User 1 promotes User 2
	_, err := client.UpdateChannelRoles(channel.Id, th.BasicUser2.Id, ChannelAdmin)
	require.NoError(t, err)

	member, _, err := client.GetChannelMember(channel.Id, th.BasicUser2.Id, "")
	require.NoError(t, err)
	require.Equal(t, ChannelAdmin, member.Roles, "roles don't match")

	// User 1 demotes User 2
	_, err = client.UpdateChannelRoles(channel.Id, th.BasicUser2.Id, ChannelMember)
	require.NoError(t, err)

	th.LoginBasic2()

	// User 2 cannot demote User 1
	resp, err := client.UpdateChannelRoles(channel.Id, th.BasicUser.Id, ChannelMember)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	// User 2 cannot promote self
	resp, err = client.UpdateChannelRoles(channel.Id, th.BasicUser2.Id, ChannelAdmin)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	th.LoginBasic()

	// User 1 demotes self
	_, err = client.UpdateChannelRoles(channel.Id, th.BasicUser.Id, ChannelMember)
	require.NoError(t, err)

	// System Admin promotes User 1
	_, err = th.SystemAdminClient.UpdateChannelRoles(channel.Id, th.BasicUser.Id, ChannelAdmin)
	require.NoError(t, err)

	// System Admin demotes User 1
	_, err = th.SystemAdminClient.UpdateChannelRoles(channel.Id, th.BasicUser.Id, ChannelMember)
	require.NoError(t, err)

	// System Admin promotes User 1
	_, err = th.SystemAdminClient.UpdateChannelRoles(channel.Id, th.BasicUser.Id, ChannelAdmin)
	require.NoError(t, err)

	th.LoginBasic()

	resp, err = client.UpdateChannelRoles(channel.Id, th.BasicUser.Id, "junk")
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	resp, err = client.UpdateChannelRoles(channel.Id, "junk", ChannelMember)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	resp, err = client.UpdateChannelRoles("junk", th.BasicUser.Id, ChannelMember)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	resp, err = client.UpdateChannelRoles(channel.Id, model.NewId(), ChannelMember)
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	resp, err = client.UpdateChannelRoles(model.NewId(), th.BasicUser.Id, ChannelMember)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)
}

func TestUpdateChannelMemberSchemeRoles(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	SystemAdminClient := th.SystemAdminClient
	WebSocketClient, err := th.CreateWebSocketClient()
	WebSocketClient.Listen()
	require.NoError(t, err)

	th.LoginBasic()

	s1 := &model.SchemeRoles{
		SchemeAdmin: false,
		SchemeUser:  false,
		SchemeGuest: false,
	}
	_, err = SystemAdminClient.UpdateChannelMemberSchemeRoles(th.BasicChannel.Id, th.BasicUser.Id, s1)
	require.NoError(t, err)

	timeout := time.After(600 * time.Millisecond)
	waiting := true
	for waiting {
		select {
		case event := <-WebSocketClient.EventChannel:
			if event.EventType() == model.WebsocketEventChannelMemberUpdated {
				require.Equal(t, model.WebsocketEventChannelMemberUpdated, event.EventType())
				waiting = false
			}
		case <-timeout:
			require.Fail(t, "Should have received event channel member websocket event and not timedout")
			waiting = false
		}
	}

	tm1, _, err := SystemAdminClient.GetChannelMember(th.BasicChannel.Id, th.BasicUser.Id, "")
	require.NoError(t, err)
	assert.Equal(t, false, tm1.SchemeGuest)
	assert.Equal(t, false, tm1.SchemeUser)
	assert.Equal(t, false, tm1.SchemeAdmin)

	s2 := &model.SchemeRoles{
		SchemeAdmin: false,
		SchemeUser:  true,
		SchemeGuest: false,
	}
	_, err = SystemAdminClient.UpdateChannelMemberSchemeRoles(th.BasicChannel.Id, th.BasicUser.Id, s2)
	require.NoError(t, err)

	tm2, _, err := SystemAdminClient.GetChannelMember(th.BasicChannel.Id, th.BasicUser.Id, "")
	require.NoError(t, err)
	assert.Equal(t, false, tm2.SchemeGuest)
	assert.Equal(t, true, tm2.SchemeUser)
	assert.Equal(t, false, tm2.SchemeAdmin)

	s3 := &model.SchemeRoles{
		SchemeAdmin: true,
		SchemeUser:  false,
		SchemeGuest: false,
	}
	_, err = SystemAdminClient.UpdateChannelMemberSchemeRoles(th.BasicChannel.Id, th.BasicUser.Id, s3)
	require.NoError(t, err)

	tm3, _, err := SystemAdminClient.GetChannelMember(th.BasicChannel.Id, th.BasicUser.Id, "")
	require.NoError(t, err)
	assert.Equal(t, false, tm3.SchemeGuest)
	assert.Equal(t, false, tm3.SchemeUser)
	assert.Equal(t, true, tm3.SchemeAdmin)

	s4 := &model.SchemeRoles{
		SchemeAdmin: true,
		SchemeUser:  true,
		SchemeGuest: false,
	}
	_, err = SystemAdminClient.UpdateChannelMemberSchemeRoles(th.BasicChannel.Id, th.BasicUser.Id, s4)
	require.NoError(t, err)

	tm4, _, err := SystemAdminClient.GetChannelMember(th.BasicChannel.Id, th.BasicUser.Id, "")
	require.NoError(t, err)
	assert.Equal(t, false, tm4.SchemeGuest)
	assert.Equal(t, true, tm4.SchemeUser)
	assert.Equal(t, true, tm4.SchemeAdmin)

	s5 := &model.SchemeRoles{
		SchemeAdmin: false,
		SchemeUser:  false,
		SchemeGuest: true,
	}
	_, err = SystemAdminClient.UpdateChannelMemberSchemeRoles(th.BasicChannel.Id, th.BasicUser.Id, s5)
	require.NoError(t, err)

	tm5, _, err := SystemAdminClient.GetChannelMember(th.BasicChannel.Id, th.BasicUser.Id, "")
	require.NoError(t, err)
	assert.Equal(t, true, tm5.SchemeGuest)
	assert.Equal(t, false, tm5.SchemeUser)
	assert.Equal(t, false, tm5.SchemeAdmin)

	s6 := &model.SchemeRoles{
		SchemeAdmin: false,
		SchemeUser:  true,
		SchemeGuest: true,
	}
	resp, err := SystemAdminClient.UpdateChannelMemberSchemeRoles(th.BasicChannel.Id, th.BasicUser.Id, s6)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	resp, err = SystemAdminClient.UpdateChannelMemberSchemeRoles(model.NewId(), th.BasicUser.Id, s4)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	resp, err = SystemAdminClient.UpdateChannelMemberSchemeRoles(th.BasicChannel.Id, model.NewId(), s4)
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	resp, err = SystemAdminClient.UpdateChannelMemberSchemeRoles("ASDF", th.BasicUser.Id, s4)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	resp, err = SystemAdminClient.UpdateChannelMemberSchemeRoles(th.BasicChannel.Id, "ASDF", s4)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	th.LoginBasic2()
	resp, err = th.Client.UpdateChannelMemberSchemeRoles(th.BasicChannel.Id, th.BasicUser.Id, s4)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	SystemAdminClient.Logout()
	resp, err = SystemAdminClient.UpdateChannelMemberSchemeRoles(th.BasicChannel.Id, th.SystemAdminUser.Id, s4)
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)
}

func TestUpdateChannelNotifyProps(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client

	props := map[string]string{}
	props[model.DesktopNotifyProp] = model.ChannelNotifyMention
	props[model.MarkUnreadNotifyProp] = model.ChannelMarkUnreadMention

	_, err := client.UpdateChannelNotifyProps(th.BasicChannel.Id, th.BasicUser.Id, props)
	require.NoError(t, err)

	member, appErr := th.App.GetChannelMember(th.Context, th.BasicChannel.Id, th.BasicUser.Id)
	require.Nil(t, appErr)
	require.Equal(t, model.ChannelNotifyMention, member.NotifyProps[model.DesktopNotifyProp], "bad update")
	require.Equal(t, model.ChannelMarkUnreadMention, member.NotifyProps[model.MarkUnreadNotifyProp], "bad update")

	resp, err := client.UpdateChannelNotifyProps("junk", th.BasicUser.Id, props)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	resp, err = client.UpdateChannelNotifyProps(th.BasicChannel.Id, "junk", props)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	resp, err = client.UpdateChannelNotifyProps(model.NewId(), th.BasicUser.Id, props)
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	resp, err = client.UpdateChannelNotifyProps(th.BasicChannel.Id, model.NewId(), props)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	_, err = client.UpdateChannelNotifyProps(th.BasicChannel.Id, th.BasicUser.Id, map[string]string{})
	require.NoError(t, err)

	client.Logout()
	resp, err = client.UpdateChannelNotifyProps(th.BasicChannel.Id, th.BasicUser.Id, props)
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	_, err = th.SystemAdminClient.UpdateChannelNotifyProps(th.BasicChannel.Id, th.BasicUser.Id, props)
	require.NoError(t, err)
}

func TestAddChannelMember(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client
	user := th.BasicUser
	user2 := th.BasicUser2
	team := th.BasicTeam
	publicChannel := th.CreatePublicChannel()
	privateChannel := th.CreatePrivateChannel()

	user3 := th.CreateUserWithClient(th.SystemAdminClient)
	_, _, err := th.SystemAdminClient.AddTeamMember(team.Id, user3.Id)
	require.NoError(t, err)

	cm, resp, err := client.AddChannelMember(publicChannel.Id, user2.Id)
	require.NoError(t, err)
	CheckCreatedStatus(t, resp)
	require.Equal(t, publicChannel.Id, cm.ChannelId, "should have returned exact channel")
	require.Equal(t, user2.Id, cm.UserId, "should have returned exact user added to public channel")

	cm, _, err = client.AddChannelMember(privateChannel.Id, user2.Id)
	require.NoError(t, err)
	require.Equal(t, privateChannel.Id, cm.ChannelId, "should have returned exact channel")
	require.Equal(t, user2.Id, cm.UserId, "should have returned exact user added to private channel")

	post := &model.Post{ChannelId: publicChannel.Id, Message: "a" + GenerateTestId() + "a"}
	rpost, _, err := client.CreatePost(post)
	require.NoError(t, err)

	client.RemoveUserFromChannel(publicChannel.Id, user.Id)
	_, resp, err = client.AddChannelMemberWithRootId(publicChannel.Id, user.Id, rpost.Id)
	require.NoError(t, err)
	CheckCreatedStatus(t, resp)

	client.RemoveUserFromChannel(publicChannel.Id, user.Id)
	_, resp, err = client.AddChannelMemberWithRootId(publicChannel.Id, user.Id, "junk")
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	_, resp, err = client.AddChannelMemberWithRootId(publicChannel.Id, user.Id, GenerateTestId())
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	client.RemoveUserFromChannel(publicChannel.Id, user.Id)
	_, _, err = client.AddChannelMember(publicChannel.Id, user.Id)
	require.NoError(t, err)

	cm, resp, err = client.AddChannelMember(publicChannel.Id, "junk")
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)
	require.Nil(t, cm, "should return nothing")

	_, resp, err = client.AddChannelMember(publicChannel.Id, GenerateTestId())
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	_, resp, err = client.AddChannelMember("junk", user2.Id)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	_, resp, err = client.AddChannelMember(GenerateTestId(), user2.Id)
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	otherUser := th.CreateUser()
	otherChannel := th.CreatePublicChannel()
	client.Logout()
	client.Login(user2.Id, user2.Password)

	_, resp, err = client.AddChannelMember(publicChannel.Id, otherUser.Id)
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	_, resp, err = client.AddChannelMember(privateChannel.Id, otherUser.Id)
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	_, resp, err = client.AddChannelMember(otherChannel.Id, otherUser.Id)
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	client.Logout()
	client.Login(user.Id, user.Password)

	// should fail adding user who is not a member of the team
	_, resp, err = client.AddChannelMember(otherChannel.Id, otherUser.Id)
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	client.DeleteChannel(otherChannel.Id)

	// should fail adding user to a deleted channel
	_, resp, err = client.AddChannelMember(otherChannel.Id, user2.Id)
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	client.Logout()
	_, resp, err = client.AddChannelMember(publicChannel.Id, user2.Id)
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	_, resp, err = client.AddChannelMember(privateChannel.Id, user2.Id)
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)

	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
		_, _, err = client.AddChannelMember(publicChannel.Id, user2.Id)
		require.NoError(t, err)

		_, _, err = client.AddChannelMember(privateChannel.Id, user2.Id)
		require.NoError(t, err)
	})

	// Check the appropriate permissions are enforced.
	defaultRolePermissions := th.SaveDefaultRolePermissions()
	defer func() {
		th.RestoreDefaultRolePermissions(defaultRolePermissions)
	}()

	th.AddPermissionToRole(model.PermissionManagePrivateChannelMembers.Id, model.ChannelUserRoleId)

	// Check that a regular channel user can add other users.
	client.Login(user2.Username, user2.Password)
	privateChannel = th.CreatePrivateChannel()
	_, _, err = client.AddChannelMember(privateChannel.Id, user.Id)
	require.NoError(t, err)
	client.Logout()

	client.Login(user.Username, user.Password)
	_, _, err = client.AddChannelMember(privateChannel.Id, user3.Id)
	require.NoError(t, err)
	client.Logout()

	// Restrict the permission for adding users to Channel Admins
	th.AddPermissionToRole(model.PermissionManagePrivateChannelMembers.Id, model.ChannelAdminRoleId)
	th.RemovePermissionFromRole(model.PermissionManagePrivateChannelMembers.Id, model.ChannelUserRoleId)

	client.Login(user2.Username, user2.Password)
	privateChannel = th.CreatePrivateChannel()
	_, _, err = client.AddChannelMember(privateChannel.Id, user.Id)
	require.NoError(t, err)
	client.Logout()

	client.Login(user.Username, user.Password)
	_, resp, err = client.AddChannelMember(privateChannel.Id, user3.Id)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)
	client.Logout()

	th.MakeUserChannelAdmin(user, privateChannel)
	th.App.Srv().InvalidateAllCaches()

	client.Login(user.Username, user.Password)
	_, _, err = client.AddChannelMember(privateChannel.Id, user3.Id)
	require.NoError(t, err)
	client.Logout()

	// Set a channel to group-constrained
	privateChannel.GroupConstrained = model.NewBool(true)
	_, appErr := th.App.UpdateChannel(th.Context, privateChannel)
	require.Nil(t, appErr)

	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
		// User is not in associated groups so shouldn't be allowed
		_, _, err = client.AddChannelMember(privateChannel.Id, user.Id)
		CheckErrorID(t, err, "api.channel.add_members.user_denied")
	})

	// Associate group to team
	_, appErr = th.App.UpsertGroupSyncable(&model.GroupSyncable{
		GroupId:    th.Group.Id,
		SyncableId: privateChannel.Id,
		Type:       model.GroupSyncableTypeChannel,
	})
	require.Nil(t, appErr)

	// Add user to group
	_, appErr = th.App.UpsertGroupMember(th.Group.Id, user.Id)
	require.Nil(t, appErr)

	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
		_, _, err = client.AddChannelMember(privateChannel.Id, user.Id)
		require.NoError(t, err)
	})
}

func TestAddChannelMemberFromThread(t *testing.T) {
	t.Skip("MM-41285")
	th := Setup(t).InitBasic()
	defer th.TearDown()
	team := th.BasicTeam
	user := th.BasicUser
	user2 := th.BasicUser2
	user3 := th.CreateUserWithClient(th.SystemAdminClient)
	_, _, err := th.SystemAdminClient.AddTeamMember(team.Id, user3.Id)
	require.NoError(t, err)

	wsClient, err2 := th.CreateWebSocketClient()
	require.NoError(t, err2)
	defer wsClient.Close()
	wsClient.Listen()

	publicChannel := th.CreatePublicChannel()

	_, resp, err := th.Client.AddChannelMember(publicChannel.Id, user3.Id)
	require.NoError(t, err)
	CheckCreatedStatus(t, resp)
	_, resp, err = th.Client.AddChannelMember(publicChannel.Id, user2.Id)
	require.NoError(t, err)
	CheckCreatedStatus(t, resp)

	post := &model.Post{
		ChannelId: publicChannel.Id,
		Message:   "A root post",
		UserId:    user3.Id,
	}
	rpost, _, err := th.SystemAdminClient.CreatePost(post)
	require.NoError(t, err)

	_, _, err = th.SystemAdminClient.CreatePost(
		&model.Post{
			ChannelId: publicChannel.Id,
			Message:   "A reply post with mention @" + user.Username,
			UserId:    user2.Id,
			RootId:    rpost.Id,
		})
	require.NoError(t, err)

	_, _, err = th.SystemAdminClient.CreatePost(
		&model.Post{
			ChannelId: publicChannel.Id,
			Message:   "Another reply post with mention @" + user.Username,
			UserId:    user2.Id,
			RootId:    rpost.Id,
		})
	require.NoError(t, err)

	// Simulate adding a user to a channel from a thread
	_, _, err = th.SystemAdminClient.AddChannelMemberWithRootId(publicChannel.Id, user.Id, rpost.Id)
	require.NoError(t, err)

	// Threadmembership should exist for added user
	ut, _, err := th.Client.GetUserThread(user.Id, team.Id, rpost.Id, false)
	require.NoError(t, err)
	// Should have two mentions. There might be a race condition
	// here between the "added user to the channel" message and the GetUserThread call
	require.LessOrEqual(t, int64(2), ut.UnreadMentions)

	var caught bool
	func() {
		for {
			select {
			case ev := <-wsClient.EventChannel:
				if ev.EventType() == model.WebsocketEventThreadUpdated {
					caught = true
					var thread model.ThreadResponse
					data := ev.GetData()
					jsonErr := json.Unmarshal([]byte(data["thread"].(string)), &thread)

					require.NoError(t, jsonErr)
					require.EqualValues(t, int64(2), thread.UnreadReplies)
					require.EqualValues(t, int64(2), thread.UnreadMentions)
					require.EqualValues(t, float64(0), data["previous_unread_replies"])
					require.EqualValues(t, float64(0), data["previous_unread_mentions"])
				}
			case <-time.After(1 * time.Second):
				return
			}
		}
	}()
	require.Truef(t, caught, "User should have received %s event", model.WebsocketEventThreadUpdated)
}

func TestAddChannelMemberAddMyself(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client
	user := th.CreateUser()
	th.LinkUserToTeam(user, th.BasicTeam)
	notMemberPublicChannel1 := th.CreatePublicChannel()
	notMemberPublicChannel2 := th.CreatePublicChannel()
	notMemberPrivateChannel := th.CreatePrivateChannel()

	memberPublicChannel := th.CreatePublicChannel()
	memberPrivateChannel := th.CreatePrivateChannel()
	th.AddUserToChannel(user, memberPublicChannel)
	th.AddUserToChannel(user, memberPrivateChannel)

	testCases := []struct {
		Name                     string
		Channel                  *model.Channel
		WithJoinPublicPermission bool
		ExpectedError            string
	}{
		{
			"Add myself to a public channel with JoinPublicChannel permission",
			notMemberPublicChannel1,
			true,
			"",
		},
		{
			"Try to add myself to a private channel with the JoinPublicChannel permission",
			notMemberPrivateChannel,
			true,
			"api.context.permissions.app_error",
		},
		{
			"Try to add myself to a public channel without the JoinPublicChannel permission",
			notMemberPublicChannel2,
			false,
			"api.context.permissions.app_error",
		},
		{
			"Add myself a public channel where I'm already a member, not having JoinPublicChannel or ManageMembers permission",
			memberPublicChannel,
			false,
			"",
		},
		{
			"Add myself a private channel where I'm already a member, not having JoinPublicChannel or ManageMembers permission",
			memberPrivateChannel,
			false,
			"",
		},
	}
	client.Login(user.Email, user.Password)
	for _, tc := range testCases {
		t.Run(tc.Name, func(t *testing.T) {

			// Check the appropriate permissions are enforced.
			defaultRolePermissions := th.SaveDefaultRolePermissions()
			defer func() {
				th.RestoreDefaultRolePermissions(defaultRolePermissions)
			}()

			if !tc.WithJoinPublicPermission {
				th.RemovePermissionFromRole(model.PermissionJoinPublicChannels.Id, model.TeamUserRoleId)
			}

			_, _, err := client.AddChannelMember(tc.Channel.Id, user.Id)
			if tc.ExpectedError == "" {
				require.NoError(t, err)
			} else {
				CheckErrorID(t, err, tc.ExpectedError)
			}
		})
	}
}

func TestRemoveChannelMember(t *testing.T) {
	th := Setup(t).InitBasic()
	user1 := th.BasicUser
	user2 := th.BasicUser2
	team := th.BasicTeam
	defer th.TearDown()
	client := th.Client

	th.App.UpdateConfig(func(cfg *model.Config) {
		*cfg.ServiceSettings.EnableBotAccountCreation = true
	})
	bot := th.CreateBotWithSystemAdminClient()
	th.App.AddUserToTeam(th.Context, team.Id, bot.UserId, "")

	_, err := client.RemoveUserFromChannel(th.BasicChannel.Id, th.BasicUser2.Id)
	require.NoError(t, err)

	resp, err := client.RemoveUserFromChannel(th.BasicChannel.Id, "junk")
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	resp, err = client.RemoveUserFromChannel(th.BasicChannel.Id, model.NewId())
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	resp, err = client.RemoveUserFromChannel(model.NewId(), th.BasicUser2.Id)
	require.Error(t, err)
	CheckNotFoundStatus(t, resp)

	th.LoginBasic2()
	resp, err = client.RemoveUserFromChannel(th.BasicChannel.Id, th.BasicUser.Id)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	t.Run("success", func(t *testing.T) {
		// Setup the system administrator to listen for websocket events from the channels.
		th.LinkUserToTeam(th.SystemAdminUser, th.BasicTeam)
		_, appErr := th.App.AddUserToChannel(th.Context, th.SystemAdminUser, th.BasicChannel, false)
		require.Nil(t, appErr)
		_, appErr = th.App.AddUserToChannel(th.Context, th.SystemAdminUser, th.BasicChannel2, false)
		require.Nil(t, appErr)
		props := map[string]string{}
		props[model.DesktopNotifyProp] = model.ChannelNotifyAll
		_, err = th.SystemAdminClient.UpdateChannelNotifyProps(th.BasicChannel.Id, th.SystemAdminUser.Id, props)
		require.NoError(t, err)
		_, err = th.SystemAdminClient.UpdateChannelNotifyProps(th.BasicChannel2.Id, th.SystemAdminUser.Id, props)
		require.NoError(t, err)

		wsClient, err2 := th.CreateWebSocketSystemAdminClient()
		require.NoError(t, err2)
		wsClient.Listen()
		var closeWsClient sync.Once
		defer closeWsClient.Do(func() {
			wsClient.Close()
		})

		wsr := <-wsClient.EventChannel
		require.Equal(t, model.WebsocketEventHello, wsr.EventType())

		// requirePost listens for websocket events and tries to find the post matching
		// the expected post's channel and message.
		requirePost := func(expectedPost *model.Post) {
			t.Helper()
			for {
				select {
				case event := <-wsClient.EventChannel:
					postData, ok := event.GetData()["post"]
					if !ok {
						continue
					}

					var post model.Post
					json.Unmarshal([]byte(postData.(string)), &post)
					if post.ChannelId == expectedPost.ChannelId && post.Message == expectedPost.Message {
						return
					}
				case <-time.After(5 * time.Second):
					require.FailNow(t, "failed to find expected post after 5 seconds")
					return
				}
			}
		}

		th.App.AddUserToChannel(th.Context, th.BasicUser2, th.BasicChannel, false)
		_, err2 = client.RemoveUserFromChannel(th.BasicChannel.Id, th.BasicUser2.Id)
		require.NoError(t, err2)

		requirePost(&model.Post{
			Message:   fmt.Sprintf("@%s left the channel.", th.BasicUser2.Username),
			ChannelId: th.BasicChannel.Id,
		})

		_, err2 = client.RemoveUserFromChannel(th.BasicChannel2.Id, th.BasicUser.Id)
		require.NoError(t, err2)
		requirePost(&model.Post{
			Message:   fmt.Sprintf("@%s removed from the channel.", th.BasicUser.Username),
			ChannelId: th.BasicChannel2.Id,
		})

		_, err2 = th.SystemAdminClient.RemoveUserFromChannel(th.BasicChannel.Id, th.BasicUser.Id)
		require.NoError(t, err2)
		requirePost(&model.Post{
			Message:   fmt.Sprintf("@%s removed from the channel.", th.BasicUser.Username),
			ChannelId: th.BasicChannel.Id,
		})

		closeWsClient.Do(func() {
			wsClient.Close()
		})
	})

	// Leave deleted channel
	th.LoginBasic()
	deletedChannel := th.CreatePublicChannel()
	th.App.AddUserToChannel(th.Context, th.BasicUser, deletedChannel, false)
	th.App.AddUserToChannel(th.Context, th.BasicUser2, deletedChannel, false)

	deletedChannel.DeleteAt = 1
	th.App.UpdateChannel(th.Context, deletedChannel)

	_, err = client.RemoveUserFromChannel(deletedChannel.Id, th.BasicUser.Id)
	require.NoError(t, err)

	th.LoginBasic()
	private := th.CreatePrivateChannel()
	th.App.AddUserToChannel(th.Context, th.BasicUser2, private, false)

	_, err = client.RemoveUserFromChannel(private.Id, th.BasicUser2.Id)
	require.NoError(t, err)

	th.LoginBasic2()
	resp, err = client.RemoveUserFromChannel(private.Id, th.BasicUser.Id)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
		th.App.AddUserToChannel(th.Context, th.BasicUser, private, false)
		_, err = client.RemoveUserFromChannel(private.Id, th.BasicUser.Id)
		require.NoError(t, err)
	})

	th.LoginBasic()
	th.UpdateUserToNonTeamAdmin(user1, team)
	th.App.Srv().InvalidateAllCaches()

	// Check the appropriate permissions are enforced.
	defaultRolePermissions := th.SaveDefaultRolePermissions()
	defer func() {
		th.RestoreDefaultRolePermissions(defaultRolePermissions)
	}()

	th.AddPermissionToRole(model.PermissionManagePrivateChannelMembers.Id, model.ChannelUserRoleId)

	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
		// Check that a regular channel user can remove other users.
		privateChannel := th.CreateChannelWithClient(client, model.ChannelTypePrivate)
		_, _, err = client.AddChannelMember(privateChannel.Id, user1.Id)
		require.NoError(t, err)
		_, _, err = client.AddChannelMember(privateChannel.Id, user2.Id)
		require.NoError(t, err)

		_, err = client.RemoveUserFromChannel(privateChannel.Id, user2.Id)
		require.NoError(t, err)
	})

	// Restrict the permission for adding users to Channel Admins
	th.AddPermissionToRole(model.PermissionManagePrivateChannelMembers.Id, model.ChannelAdminRoleId)
	th.RemovePermissionFromRole(model.PermissionManagePrivateChannelMembers.Id, model.ChannelUserRoleId)

	privateChannel := th.CreateChannelWithClient(th.SystemAdminClient, model.ChannelTypePrivate)
	_, _, err = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user1.Id)
	require.NoError(t, err)
	_, _, err = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user2.Id)
	require.NoError(t, err)
	_, _, err = th.SystemAdminClient.AddChannelMember(privateChannel.Id, bot.UserId)
	require.NoError(t, err)

	resp, err = client.RemoveUserFromChannel(privateChannel.Id, user2.Id)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	th.MakeUserChannelAdmin(user1, privateChannel)
	th.App.Srv().InvalidateAllCaches()

	_, err = client.RemoveUserFromChannel(privateChannel.Id, user2.Id)
	require.NoError(t, err)

	_, _, err = th.SystemAdminClient.AddChannelMember(privateChannel.Id, th.SystemAdminUser.Id)
	require.NoError(t, err)

	// If the channel is group-constrained the user cannot be removed
	privateChannel.GroupConstrained = model.NewBool(true)
	_, appErr := th.App.UpdateChannel(th.Context, privateChannel)
	require.Nil(t, appErr)
	_, err = client.RemoveUserFromChannel(privateChannel.Id, user2.Id)
	CheckErrorID(t, err, "api.channel.remove_member.group_constrained.app_error")

	// If the channel is group-constrained user can remove self
	_, err = th.SystemAdminClient.RemoveUserFromChannel(privateChannel.Id, th.SystemAdminUser.Id)
	require.NoError(t, err)

	// Test on preventing removal of user from a direct channel
	directChannel, _, err := client.CreateDirectChannel(user1.Id, user2.Id)
	require.NoError(t, err)

	// If the channel is group-constrained a user can remove a bot
	_, err = client.RemoveUserFromChannel(privateChannel.Id, bot.UserId)
	require.NoError(t, err)

	resp, err = client.RemoveUserFromChannel(directChannel.Id, user1.Id)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	resp, err = client.RemoveUserFromChannel(directChannel.Id, user2.Id)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	resp, err = th.SystemAdminClient.RemoveUserFromChannel(directChannel.Id, user1.Id)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	// Test on preventing removal of user from a group channel
	user3 := th.CreateUser()
	groupChannel, _, err := client.CreateGroupChannel([]string{user1.Id, user2.Id, user3.Id})
	require.NoError(t, err)

	th.TestForAllClients(t, func(t *testing.T, client *model.Client4) {
		resp, err = client.RemoveUserFromChannel(groupChannel.Id, user1.Id)
		require.Error(t, err)
		CheckBadRequestStatus(t, resp)
	})
}

func TestAutocompleteChannels(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()

	// A private channel to make sure private channels are used.
	ptown, _, _ := th.Client.CreateChannel(&model.Channel{
		DisplayName: "Town",
		Name:        "town",
		Type:        model.ChannelTypePrivate,
		TeamId:      th.BasicTeam.Id,
	})
	tower, _, _ := th.Client.CreateChannel(&model.Channel{
		DisplayName: "Tower",
		Name:        "tower",
		Type:        model.ChannelTypeOpen,
		TeamId:      th.BasicTeam.Id,
	})
	defer func() {
		th.Client.DeleteChannel(ptown.Id)
		th.Client.DeleteChannel(tower.Id)
	}()

	for _, tc := range []struct {
		description      string
		teamId           string
		fragment         string
		expectedIncludes []string
		expectedExcludes []string
	}{
		{
			"Basic town-square",
			th.BasicTeam.Id,
			"town",
			[]string{"town-square", "town"},
			[]string{"off-topic", "tower"},
		},
		{
			"Basic off-topic",
			th.BasicTeam.Id,
			"off-to",
			[]string{"off-topic"},
			[]string{"town-square", "town", "tower"},
		},
		{
			"Basic town square and off topic",
			th.BasicTeam.Id,
			"tow",
			[]string{"town-square", "tower", "town"},
			[]string{"off-topic"},
		},
	} {
		t.Run(tc.description, func(t *testing.T) {
			channels, _, err := th.Client.AutocompleteChannelsForTeam(tc.teamId, tc.fragment)
			require.NoError(t, err)
			names := make([]string, len(channels))
			for i, c := range channels {
				names[i] = c.Name
			}
			for _, name := range tc.expectedIncludes {
				require.Contains(t, names, name, "channel not included")
			}
			for _, name := range tc.expectedExcludes {
				require.NotContains(t, names, name, "channel not excluded")
			}
		})
	}
}

func TestAutocompleteChannelsForSearch(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()

	th.LoginSystemAdminWithClient(th.SystemAdminClient)
	th.LoginBasicWithClient(th.Client)

	u1 := th.CreateUserWithClient(th.SystemAdminClient)
	defer th.App.PermanentDeleteUser(th.Context, u1)
	u2 := th.CreateUserWithClient(th.SystemAdminClient)
	defer th.App.PermanentDeleteUser(th.Context, u2)
	u3 := th.CreateUserWithClient(th.SystemAdminClient)
	defer th.App.PermanentDeleteUser(th.Context, u3)
	u4 := th.CreateUserWithClient(th.SystemAdminClient)
	defer th.App.PermanentDeleteUser(th.Context, u4)

	// A private channel to make sure private channels are not used
	ptown, _, _ := th.SystemAdminClient.CreateChannel(&model.Channel{
		DisplayName: "Town",
		Name:        "town",
		Type:        model.ChannelTypePrivate,
		TeamId:      th.BasicTeam.Id,
	})
	defer func() {
		th.Client.DeleteChannel(ptown.Id)
	}()
	mypriv, _, _ := th.Client.CreateChannel(&model.Channel{
		DisplayName: "My private town",
		Name:        "townpriv",
		Type:        model.ChannelTypePrivate,
		TeamId:      th.BasicTeam.Id,
	})
	defer func() {
		th.Client.DeleteChannel(mypriv.Id)
	}()

	dc1, _, err := th.Client.CreateDirectChannel(th.BasicUser.Id, u1.Id)
	require.NoError(t, err)
	defer func() {
		th.Client.DeleteChannel(dc1.Id)
	}()

	dc2, _, err := th.SystemAdminClient.CreateDirectChannel(u2.Id, u3.Id)
	require.NoError(t, err)
	defer func() {
		th.SystemAdminClient.DeleteChannel(dc2.Id)
	}()

	gc1, _, err := th.Client.CreateGroupChannel([]string{th.BasicUser.Id, u2.Id, u3.Id})
	require.NoError(t, err)
	defer func() {
		th.Client.DeleteChannel(gc1.Id)
	}()

	gc2, _, err := th.SystemAdminClient.CreateGroupChannel([]string{u2.Id, u3.Id, u4.Id})
	require.NoError(t, err)
	defer func() {
		th.SystemAdminClient.DeleteChannel(gc2.Id)
	}()

	for _, tc := range []struct {
		description      string
		teamID           string
		fragment         string
		expectedIncludes []string
		expectedExcludes []string
	}{
		{
			"Basic town-square",
			th.BasicTeam.Id,
			"town",
			[]string{"town-square", "townpriv"},
			[]string{"off-topic", "town"},
		},
		{
			"Basic off-topic",
			th.BasicTeam.Id,
			"off-to",
			[]string{"off-topic"},
			[]string{"town-square", "town", "townpriv"},
		},
		{
			"Basic town square and townpriv",
			th.BasicTeam.Id,
			"tow",
			[]string{"town-square", "townpriv"},
			[]string{"off-topic", "town"},
		},
		{
			"Direct and group messages",
			th.BasicTeam.Id,
			"fakeuser",
			[]string{dc1.Name, gc1.Name},
			[]string{dc2.Name, gc2.Name},
		},
	} {
		t.Run(tc.description, func(t *testing.T) {
			channels, _, err := th.Client.AutocompleteChannelsForTeamForSearch(tc.teamID, tc.fragment)
			require.NoError(t, err)
			names := make([]string, len(channels))
			for i, c := range channels {
				names[i] = c.Name
			}
			for _, name := range tc.expectedIncludes {
				require.Contains(t, names, name, "channel not included")
			}
			for _, name := range tc.expectedExcludes {
				require.NotContains(t, names, name, "channel not excluded")
			}
		})
	}
}

func TestAutocompleteChannelsForSearchGuestUsers(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()

	u1 := th.CreateUserWithClient(th.SystemAdminClient)
	defer th.App.PermanentDeleteUser(th.Context, u1)

	enableGuestAccounts := *th.App.Config().GuestAccountsSettings.Enable
	defer func() {
		th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = enableGuestAccounts })
		th.App.Srv().RemoveLicense()
	}()
	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = true })
	th.App.Srv().SetLicense(model.NewTestLicense())

	id := model.NewId()
	guest := &model.User{
		Email:         "success+" + id + "@simulator.amazonses.com",
		Username:      "un_" + id,
		Nickname:      "nn_" + id,
		Password:      "Password1",
		EmailVerified: true,
	}
	guest, appErr := th.App.CreateGuest(th.Context, guest)
	require.Nil(t, appErr)

	th.LoginSystemAdminWithClient(th.SystemAdminClient)

	_, _, err := th.SystemAdminClient.AddTeamMember(th.BasicTeam.Id, guest.Id)
	require.NoError(t, err)

	// A private channel to make sure private channels are not used
	town, _, _ := th.SystemAdminClient.CreateChannel(&model.Channel{
		DisplayName: "Town",
		Name:        "town",
		Type:        model.ChannelTypeOpen,
		TeamId:      th.BasicTeam.Id,
	})
	defer func() {
		th.SystemAdminClient.DeleteChannel(town.Id)
	}()
	_, _, err = th.SystemAdminClient.AddChannelMember(town.Id, guest.Id)
	require.NoError(t, err)

	mypriv, _, _ := th.SystemAdminClient.CreateChannel(&model.Channel{
		DisplayName: "My private town",
		Name:        "townpriv",
		Type:        model.ChannelTypePrivate,
		TeamId:      th.BasicTeam.Id,
	})
	defer func() {
		th.SystemAdminClient.DeleteChannel(mypriv.Id)
	}()
	_, _, err = th.SystemAdminClient.AddChannelMember(mypriv.Id, guest.Id)
	require.NoError(t, err)

	dc1, _, err := th.SystemAdminClient.CreateDirectChannel(th.BasicUser.Id, guest.Id)
	require.NoError(t, err)
	defer func() {
		th.SystemAdminClient.DeleteChannel(dc1.Id)
	}()

	dc2, _, err := th.SystemAdminClient.CreateDirectChannel(th.BasicUser.Id, th.BasicUser2.Id)
	require.NoError(t, err)
	defer func() {
		th.SystemAdminClient.DeleteChannel(dc2.Id)
	}()

	gc1, _, err := th.SystemAdminClient.CreateGroupChannel([]string{th.BasicUser.Id, th.BasicUser2.Id, guest.Id})
	require.NoError(t, err)
	defer func() {
		th.SystemAdminClient.DeleteChannel(gc1.Id)
	}()

	gc2, _, err := th.SystemAdminClient.CreateGroupChannel([]string{th.BasicUser.Id, th.BasicUser2.Id, u1.Id})
	require.NoError(t, err)
	defer func() {
		th.SystemAdminClient.DeleteChannel(gc2.Id)
	}()

	_, _, err = th.Client.Login(guest.Username, "Password1")
	require.NoError(t, err)

	for _, tc := range []struct {
		description      string
		teamID           string
		fragment         string
		expectedIncludes []string
		expectedExcludes []string
	}{
		{
			"Should return those channel where is member",
			th.BasicTeam.Id,
			"town",
			[]string{"town", "townpriv"},
			[]string{"town-square", "off-topic"},
		},
		{
			"Should return empty if not member of the searched channels",
			th.BasicTeam.Id,
			"off-to",
			[]string{},
			[]string{"off-topic", "town-square", "town", "townpriv"},
		},
		{
			"Should return direct and group messages",
			th.BasicTeam.Id,
			"fakeuser",
			[]string{dc1.Name, gc1.Name},
			[]string{dc2.Name, gc2.Name},
		},
	} {
		t.Run(tc.description, func(t *testing.T) {
			channels, _, err := th.Client.AutocompleteChannelsForTeamForSearch(tc.teamID, tc.fragment)
			require.NoError(t, err)
			names := make([]string, len(channels))
			for i, c := range channels {
				names[i] = c.Name
			}
			for _, name := range tc.expectedIncludes {
				require.Contains(t, names, name, "channel not included")
			}
			for _, name := range tc.expectedExcludes {
				require.NotContains(t, names, name, "channel not excluded")
			}
		})
	}
}

func TestUpdateChannelScheme(t *testing.T) {
	th := Setup(t)
	defer th.TearDown()

	th.App.Srv().SetLicense(model.NewTestLicense(""))

	th.App.SetPhase2PermissionsMigrationStatus(true)

	team, _, err := th.SystemAdminClient.CreateTeam(&model.Team{
		DisplayName:     "Name",
		Description:     "Some description",
		CompanyName:     "Some company name",
		AllowOpenInvite: false,
		InviteId:        "inviteid0",
		Name:            "z-z-" + model.NewId() + "a",
		Email:           "success+" + model.NewId() + "@simulator.amazonses.com",
		Type:            model.TeamOpen,
	})
	require.NoError(t, err)

	channel, _, err := th.SystemAdminClient.CreateChannel(&model.Channel{
		DisplayName: "Name",
		Name:        "z-z-" + model.NewId() + "a",
		Type:        model.ChannelTypeOpen,
		TeamId:      team.Id,
	})
	require.NoError(t, err)

	channelScheme, _, err := th.SystemAdminClient.CreateScheme(&model.Scheme{
		DisplayName: "DisplayName",
		Name:        model.NewId(),
		Description: "Some description",
		Scope:       model.SchemeScopeChannel,
	})
	require.NoError(t, err)

	teamScheme, _, err := th.SystemAdminClient.CreateScheme(&model.Scheme{
		DisplayName: "DisplayName",
		Name:        model.NewId(),
		Description: "Some description",
		Scope:       model.SchemeScopeTeam,
	})
	require.NoError(t, err)

	// Test the setup/base case.
	_, err = th.SystemAdminClient.UpdateChannelScheme(channel.Id, channelScheme.Id)
	require.NoError(t, err)

	// Test various invalid channel and scheme id combinations.
	resp, err := th.SystemAdminClient.UpdateChannelScheme(channel.Id, "x")
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)
	resp, err = th.SystemAdminClient.UpdateChannelScheme("x", channelScheme.Id)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)
	resp, err = th.SystemAdminClient.UpdateChannelScheme("x", "x")
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	// Test that permissions are required.
	resp, err = th.Client.UpdateChannelScheme(channel.Id, channelScheme.Id)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)

	// Test that a license is required.
	th.App.Srv().SetLicense(nil)
	resp, err = th.SystemAdminClient.UpdateChannelScheme(channel.Id, channelScheme.Id)
	require.Error(t, err)
	CheckForbiddenStatus(t, resp)
	th.App.Srv().SetLicense(model.NewTestLicense(""))

	// Test an invalid scheme scope.
	resp, err = th.SystemAdminClient.UpdateChannelScheme(channel.Id, teamScheme.Id)
	require.Error(t, err)
	CheckBadRequestStatus(t, resp)

	// Test that an unauthenticated user gets rejected.
	th.SystemAdminClient.Logout()
	resp, err = th.SystemAdminClient.UpdateChannelScheme(channel.Id, channelScheme.Id)
	require.Error(t, err)
	CheckUnauthorizedStatus(t, resp)
}

func TestGetChannelMembersTimezones(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()
	client := th.Client

	user := th.BasicUser
	user.Timezone["useAutomaticTimezone"] = "false"
	user.Timezone["manualTimezone"] = "XOXO/BLABLA"
	_, _, err := client.UpdateUser(user)
	require.NoError(t, err)

	user2 := th.BasicUser2
	user2.Timezone["automaticTimezone"] = "NoWhere/Island"
	_, _, err = th.SystemAdminClient.UpdateUser(user2)
	require.NoError(t, err)

	timezone, _, err := client.GetChannelMembersTimezones(th.BasicChannel.Id)
	require.NoError(t, err)
	require.Len(t, timezone, 2, "should return 2 timezones")

	//both users have same timezone
	user2.Timezone["automaticTimezone"] = "XOXO/BLABLA"
	_, _, err = th.SystemAdminClient.UpdateUser(user2)
	require.NoError(t, err)

	timezone, _, err = client.GetChannelMembersTimezones(th.BasicChannel.Id)
	require.NoError(t, err)
	require.Len(t, timezone, 1, "should return 1 timezone")

	//no timezone set should return empty
	user2.Timezone["automaticTimezone"] = ""
	_, _, err = th.SystemAdminClient.UpdateUser(user2)
	require.NoError(t, err)

	user.Timezone["manualTimezone"] = ""
	_, _, err = client.UpdateUser(user)
	require.NoError(t, err)

	timezone, _, err = client.GetChannelMembersTimezones(th.BasicChannel.Id)
	require.NoError(t, err)
	require.Empty(t, timezone, "should return 0 timezone")
}

func TestChannelMembersMinusGroupMembers(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()

	user1 := th.BasicUser
	user2 := th.BasicUser2

	channel := th.CreatePrivateChannel()

	_, appErr := th.App.AddChannelMember(th.Context, user1.Id, channel, app.ChannelMemberOpts{})
	require.Nil(t, appErr)
	_, appErr = th.App.AddChannelMember(th.Context, user2.Id, channel, app.ChannelMemberOpts{})
	require.Nil(t, appErr)

	channel.GroupConstrained = model.NewBool(true)
	channel, appErr = th.App.UpdateChannel(th.Context, channel)
	require.Nil(t, appErr)

	group1 := th.CreateGroup()
	group2 := th.CreateGroup()

	_, appErr = th.App.UpsertGroupMember(group1.Id, user1.Id)
	require.Nil(t, appErr)
	_, appErr = th.App.UpsertGroupMember(group2.Id, user2.Id)
	require.Nil(t, appErr)

	// No permissions
	_, _, _, err := th.Client.ChannelMembersMinusGroupMembers(channel.Id, []string{group1.Id, group2.Id}, 0, 100, "")
	CheckErrorID(t, err, "api.context.permissions.app_error")

	testCases := map[string]struct {
		groupIDs        []string
		page            int
		perPage         int
		length          int
		count           int
		otherAssertions func([]*model.UserWithGroups)
	}{
		"All groups, expect no users removed": {
			groupIDs: []string{group1.Id, group2.Id},
			page:     0,
			perPage:  100,
			length:   0,
			count:    0,
		},
		"Some nonexistent group, page 0": {
			groupIDs: []string{model.NewId()},
			page:     0,
			perPage:  1,
			length:   1,
			count:    2,
		},
		"Some nonexistent group, page 1": {
			groupIDs: []string{model.NewId()},
			page:     1,
			perPage:  1,
			length:   1,
			count:    2,
		},
		"One group, expect one user removed": {
			groupIDs: []string{group1.Id},
			page:     0,
			perPage:  100,
			length:   1,
			count:    1,
			otherAssertions: func(uwg []*model.UserWithGroups) {
				require.Equal(t, uwg[0].Id, user2.Id)
			},
		},
		"Other group, expect other user removed": {
			groupIDs: []string{group2.Id},
			page:     0,
			perPage:  100,
			length:   1,
			count:    1,
			otherAssertions: func(uwg []*model.UserWithGroups) {
				require.Equal(t, uwg[0].Id, user1.Id)
			},
		},
	}

	for name, tc := range testCases {
		t.Run(name, func(t *testing.T) {
			uwg, count, _, err := th.SystemAdminClient.ChannelMembersMinusGroupMembers(channel.Id, tc.groupIDs, tc.page, tc.perPage, "")
			require.NoError(t, err)
			require.Len(t, uwg, tc.length)
			require.Equal(t, tc.count, int(count))
			if tc.otherAssertions != nil {
				tc.otherAssertions(uwg)
			}
		})
	}
}

func TestGetChannelModerations(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()

	channel := th.BasicChannel
	team := th.BasicTeam

	th.App.SetPhase2PermissionsMigrationStatus(true)

	t.Run("Errors without a license", func(t *testing.T) {
		_, _, err := th.SystemAdminClient.GetChannelModerations(channel.Id, "")
		CheckErrorID(t, err, "api.channel.get_channel_moderations.license.error")
	})

	th.App.Srv().SetLicense(model.NewTestLicense())

	t.Run("Errors as a non sysadmin", func(t *testing.T) {
		_, _, err := th.Client.GetChannelModerations(channel.Id, "")
		CheckErrorID(t, err, "api.context.permissions.app_error")
	})

	th.App.Srv().SetLicense(model.NewTestLicense())

	t.Run("Returns default moderations with default roles", func(t *testing.T) {
		moderations, _, err := th.SystemAdminClient.GetChannelModerations(channel.Id, "")
		require.NoError(t, err)
		require.Equal(t, len(moderations), 4)
		for _, moderation := range moderations {
			if moderation.Name == "manage_members" {
				require.Empty(t, moderation.Roles.Guests)
			} else {
				require.Equal(t, moderation.Roles.Guests.Value, true)
				require.Equal(t, moderation.Roles.Guests.Enabled, true)
			}

			require.Equal(t, moderation.Roles.Members.Value, true)
			require.Equal(t, moderation.Roles.Members.Enabled, true)
		}
	})

	t.Run("Returns value false and enabled false for permissions that are not present in higher scoped scheme when no channel scheme present", func(t *testing.T) {
		scheme := th.SetupTeamScheme()
		team.SchemeId = &scheme.Id
		_, appErr := th.App.UpdateTeamScheme(team)
		require.Nil(t, appErr)

		th.RemovePermissionFromRole(model.PermissionCreatePost.Id, scheme.DefaultChannelGuestRole)
		defer th.AddPermissionToRole(model.PermissionCreatePost.Id, scheme.DefaultChannelGuestRole)

		moderations, _, err := th.SystemAdminClient.GetChannelModerations(channel.Id, "")
		require.NoError(t, err)
		for _, moderation := range moderations {
			if moderation.Name == model.PermissionCreatePost.Id {
				require.Equal(t, moderation.Roles.Members.Value, true)
				require.Equal(t, moderation.Roles.Members.Enabled, true)
				require.Equal(t, moderation.Roles.Guests.Value, false)
				require.Equal(t, moderation.Roles.Guests.Enabled, false)
			}
		}
	})

	t.Run("Returns value false and enabled true for permissions that are not present in channel scheme but present in team scheme", func(t *testing.T) {
		scheme := th.SetupChannelScheme()
		channel.SchemeId = &scheme.Id
		_, appErr := th.App.UpdateChannelScheme(th.Context, channel)
		require.Nil(t, appErr)

		th.RemovePermissionFromRole(model.PermissionCreatePost.Id, scheme.DefaultChannelGuestRole)
		defer th.AddPermissionToRole(model.PermissionCreatePost.Id, scheme.DefaultChannelGuestRole)

		moderations, _, err := th.SystemAdminClient.GetChannelModerations(channel.Id, "")
		require.NoError(t, err)
		for _, moderation := range moderations {
			if moderation.Name == model.PermissionCreatePost.Id {
				require.Equal(t, moderation.Roles.Members.Value, true)
				require.Equal(t, moderation.Roles.Members.Enabled, true)
				require.Equal(t, moderation.Roles.Guests.Value, false)
				require.Equal(t, moderation.Roles.Guests.Enabled, true)
			}
		}
	})

	t.Run("Returns value false and enabled false for permissions that are not present in channel & team scheme", func(t *testing.T) {
		teamScheme := th.SetupTeamScheme()
		team.SchemeId = &teamScheme.Id
		th.App.UpdateTeamScheme(team)

		scheme := th.SetupChannelScheme()
		channel.SchemeId = &scheme.Id
		th.App.UpdateChannelScheme(th.Context, channel)

		th.RemovePermissionFromRole(model.PermissionCreatePost.Id, scheme.DefaultChannelGuestRole)
		th.RemovePermissionFromRole(model.PermissionCreatePost.Id, teamScheme.DefaultChannelGuestRole)

		defer th.AddPermissionToRole(model.PermissionCreatePost.Id, scheme.DefaultChannelGuestRole)
		defer th.AddPermissionToRole(model.PermissionCreatePost.Id, teamScheme.DefaultChannelGuestRole)

		moderations, _, err := th.SystemAdminClient.GetChannelModerations(channel.Id, "")
		require.NoError(t, err)
		for _, moderation := range moderations {
			if moderation.Name == model.PermissionCreatePost.Id {
				require.Equal(t, moderation.Roles.Members.Value, true)
				require.Equal(t, moderation.Roles.Members.Enabled, true)
				require.Equal(t, moderation.Roles.Guests.Value, false)
				require.Equal(t, moderation.Roles.Guests.Enabled, false)
			}
		}
	})

	t.Run("Returns the correct value for manage_members depending on whether the channel is public or private", func(t *testing.T) {
		scheme := th.SetupTeamScheme()
		team.SchemeId = &scheme.Id
		_, appErr := th.App.UpdateTeamScheme(team)
		require.Nil(t, appErr)

		th.RemovePermissionFromRole(model.PermissionManagePublicChannelMembers.Id, scheme.DefaultChannelUserRole)
		defer th.AddPermissionToRole(model.PermissionCreatePost.Id, scheme.DefaultChannelUserRole)

		// public channel does not have the permission
		moderations, _, err := th.SystemAdminClient.GetChannelModerations(channel.Id, "")
		require.NoError(t, err)
		for _, moderation := range moderations {
			if moderation.Name == "manage_members" {
				require.Equal(t, moderation.Roles.Members.Value, false)
			}
		}

		// private channel does have the permission
		moderations, _, err = th.SystemAdminClient.GetChannelModerations(th.BasicPrivateChannel.Id, "")
		require.NoError(t, err)
		for _, moderation := range moderations {
			if moderation.Name == "manage_members" {
				require.Equal(t, moderation.Roles.Members.Value, true)
			}
		}
	})

	t.Run("Does not return an error if the team scheme has a blank DefaultChannelGuestRole field", func(t *testing.T) {
		scheme := th.SetupTeamScheme()
		scheme.DefaultChannelGuestRole = ""

		mockStore := mocks.Store{}
		mockSchemeStore := mocks.SchemeStore{}
		mockSchemeStore.On("Get", mock.Anything).Return(scheme, nil)
		mockStore.On("Scheme").Return(&mockSchemeStore)
		mockStore.On("Team").Return(th.App.Srv().Store().Team())
		mockStore.On("Channel").Return(th.App.Srv().Store().Channel())
		mockStore.On("User").Return(th.App.Srv().Store().User())
		mockStore.On("Post").Return(th.App.Srv().Store().Post())
		mockStore.On("FileInfo").Return(th.App.Srv().Store().FileInfo())
		mockStore.On("Webhook").Return(th.App.Srv().Store().Webhook())
		mockStore.On("System").Return(th.App.Srv().Store().System())
		mockStore.On("License").Return(th.App.Srv().Store().License())
		mockStore.On("Role").Return(th.App.Srv().Store().Role())
		mockStore.On("Close").Return(nil)
		th.App.Srv().SetStore(&mockStore)

		team.SchemeId = &scheme.Id
		_, appErr := th.App.UpdateTeamScheme(team)
		require.Nil(t, appErr)

		_, _, err := th.SystemAdminClient.GetChannelModerations(channel.Id, "")
		require.NoError(t, err)
	})
}

func TestPatchChannelModerations(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()

	channel := th.BasicChannel

	emptyPatch := []*model.ChannelModerationPatch{}

	createPosts := model.ChannelModeratedPermissions[0]

	th.App.SetPhase2PermissionsMigrationStatus(true)

	t.Run("Errors without a license", func(t *testing.T) {
		_, _, err := th.SystemAdminClient.PatchChannelModerations(channel.Id, emptyPatch)
		CheckErrorID(t, err, "api.channel.patch_channel_moderations.license.error")
	})

	th.App.Srv().SetLicense(model.NewTestLicense())

	t.Run("Errors as a non sysadmin", func(t *testing.T) {
		_, _, err := th.Client.PatchChannelModerations(channel.Id, emptyPatch)
		CheckErrorID(t, err, "api.context.permissions.app_error")
	})

	th.App.Srv().SetLicense(model.NewTestLicense())

	t.Run("Returns default moderations with empty patch", func(t *testing.T) {
		moderations, _, err := th.SystemAdminClient.PatchChannelModerations(channel.Id, emptyPatch)
		require.NoError(t, err)
		require.Equal(t, len(moderations), 4)
		for _, moderation := range moderations {
			if moderation.Name == "manage_members" {
				require.Empty(t, moderation.Roles.Guests)
			} else {
				require.Equal(t, moderation.Roles.Guests.Value, true)
				require.Equal(t, moderation.Roles.Guests.Enabled, true)
			}

			require.Equal(t, moderation.Roles.Members.Value, true)
			require.Equal(t, moderation.Roles.Members.Enabled, true)
		}

		require.Nil(t, channel.SchemeId)
	})

	t.Run("Creates a scheme and returns the updated channel moderations when patching an existing permission", func(t *testing.T) {
		patch := []*model.ChannelModerationPatch{
			{
				Name:  &createPosts,
				Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(false)},
			},
		}

		moderations, _, err := th.SystemAdminClient.PatchChannelModerations(channel.Id, patch)
		require.NoError(t, err)
		require.Equal(t, len(moderations), 4)
		for _, moderation := range moderations {
			if moderation.Name == "manage_members" {
				require.Empty(t, moderation.Roles.Guests)
			} else {
				require.Equal(t, moderation.Roles.Guests.Value, true)
				require.Equal(t, moderation.Roles.Guests.Enabled, true)
			}

			if moderation.Name == createPosts {
				require.Equal(t, moderation.Roles.Members.Value, false)
				require.Equal(t, moderation.Roles.Members.Enabled, true)
			} else {
				require.Equal(t, moderation.Roles.Members.Value, true)
				require.Equal(t, moderation.Roles.Members.Enabled, true)
			}
		}
		channel, _ = th.App.GetChannel(th.Context, channel.Id)
		require.NotNil(t, channel.SchemeId)
	})

	t.Run("Removes the existing scheme when moderated permissions are set back to higher scoped values", func(t *testing.T) {
		channel, _ = th.App.GetChannel(th.Context, channel.Id)
		schemeId := channel.SchemeId

		scheme, _ := th.App.GetScheme(*schemeId)
		require.Equal(t, scheme.DeleteAt, int64(0))

		patch := []*model.ChannelModerationPatch{
			{
				Name:  &createPosts,
				Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(true)},
			},
		}

		moderations, _, err := th.SystemAdminClient.PatchChannelModerations(channel.Id, patch)
		require.NoError(t, err)
		require.Equal(t, len(moderations), 4)
		for _, moderation := range moderations {
			if moderation.Name == "manage_members" {
				require.Empty(t, moderation.Roles.Guests)
			} else {
				require.Equal(t, moderation.Roles.Guests.Value, true)
				require.Equal(t, moderation.Roles.Guests.Enabled, true)
			}

			require.Equal(t, moderation.Roles.Members.Value, true)
			require.Equal(t, moderation.Roles.Members.Enabled, true)
		}

		channel, _ = th.App.GetChannel(th.Context, channel.Id)
		require.Nil(t, channel.SchemeId)

		scheme, _ = th.App.GetScheme(*schemeId)
		require.NotEqual(t, scheme.DeleteAt, int64(0))
	})

	t.Run("Does not return an error if the team scheme has a blank DefaultChannelGuestRole field", func(t *testing.T) {
		team := th.BasicTeam
		scheme := th.SetupTeamScheme()
		scheme.DefaultChannelGuestRole = ""

		mockStore := mocks.Store{}
		mockSchemeStore := mocks.SchemeStore{}
		mockSchemeStore.On("Get", mock.Anything).Return(scheme, nil)
		mockSchemeStore.On("Save", mock.Anything).Return(scheme, nil)
		mockSchemeStore.On("Delete", mock.Anything).Return(scheme, nil)
		mockStore.On("Scheme").Return(&mockSchemeStore)
		mockStore.On("Team").Return(th.App.Srv().Store().Team())
		mockStore.On("Channel").Return(th.App.Srv().Store().Channel())
		mockStore.On("User").Return(th.App.Srv().Store().User())
		mockStore.On("Post").Return(th.App.Srv().Store().Post())
		mockStore.On("FileInfo").Return(th.App.Srv().Store().FileInfo())
		mockStore.On("Webhook").Return(th.App.Srv().Store().Webhook())
		mockStore.On("System").Return(th.App.Srv().Store().System())
		mockStore.On("License").Return(th.App.Srv().Store().License())
		mockStore.On("Role").Return(th.App.Srv().Store().Role())
		mockStore.On("Close").Return(nil)
		th.App.Srv().SetStore(&mockStore)

		team.SchemeId = &scheme.Id
		_, appErr := th.App.UpdateTeamScheme(team)
		require.Nil(t, appErr)

		moderations, _, err := th.SystemAdminClient.PatchChannelModerations(channel.Id, emptyPatch)
		require.NoError(t, err)
		require.Equal(t, len(moderations), 4)
		for _, moderation := range moderations {
			if moderation.Name == "manage_members" {
				require.Empty(t, moderation.Roles.Guests)
			} else {
				require.Equal(t, moderation.Roles.Guests.Value, false)
				require.Equal(t, moderation.Roles.Guests.Enabled, false)
			}

			require.Equal(t, moderation.Roles.Members.Value, true)
			require.Equal(t, moderation.Roles.Members.Enabled, true)
		}

		patch := []*model.ChannelModerationPatch{
			{
				Name:  &createPosts,
				Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(true)},
			},
		}

		moderations, _, err = th.SystemAdminClient.PatchChannelModerations(channel.Id, patch)
		require.NoError(t, err)
		require.Equal(t, len(moderations), 4)
		for _, moderation := range moderations {
			if moderation.Name == "manage_members" {
				require.Empty(t, moderation.Roles.Guests)
			} else {
				require.Equal(t, moderation.Roles.Guests.Value, false)
				require.Equal(t, moderation.Roles.Guests.Enabled, false)
			}

			require.Equal(t, moderation.Roles.Members.Value, true)
			require.Equal(t, moderation.Roles.Members.Enabled, true)
		}
	})

}

func TestGetChannelMemberCountsByGroup(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()

	channel := th.BasicChannel
	t.Run("Errors without a license", func(t *testing.T) {
		_, _, err := th.SystemAdminClient.GetChannelMemberCountsByGroup(channel.Id, false, "")
		CheckErrorID(t, err, "api.channel.channel_member_counts_by_group.license.error")
	})

	th.App.Srv().SetLicense(model.NewTestLicense())

	t.Run("Errors without read permission to the channel", func(t *testing.T) {
		_, _, err := th.Client.GetChannelMemberCountsByGroup(model.NewId(), false, "")
		CheckErrorID(t, err, "api.context.permissions.app_error")
	})

	t.Run("Returns empty for a channel with no members or groups", func(t *testing.T) {
		memberCounts, _, _ := th.SystemAdminClient.GetChannelMemberCountsByGroup(channel.Id, false, "")
		require.Equal(t, []*model.ChannelMemberCountByGroup{}, memberCounts)
	})

	user := th.BasicUser
	user.Timezone["useAutomaticTimezone"] = "false"
	user.Timezone["manualTimezone"] = "XOXO/BLABLA"
	_, appErr := th.App.UpsertGroupMember(th.Group.Id, user.Id)
	require.Nil(t, appErr)
	_, _, err := th.SystemAdminClient.UpdateUser(user)
	require.NoError(t, err)

	user2 := th.BasicUser2
	user2.Timezone["automaticTimezone"] = "NoWhere/Island"
	_, appErr = th.App.UpsertGroupMember(th.Group.Id, user2.Id)
	require.Nil(t, appErr)
	_, _, err = th.SystemAdminClient.UpdateUser(user2)
	require.NoError(t, err)

	t.Run("Returns users in group without timezones", func(t *testing.T) {
		memberCounts, _, _ := th.SystemAdminClient.GetChannelMemberCountsByGroup(channel.Id, false, "")
		expectedMemberCounts := []*model.ChannelMemberCountByGroup{
			{
				GroupId:                     th.Group.Id,
				ChannelMemberCount:          2,
				ChannelMemberTimezonesCount: 0,
			},
		}
		require.Equal(t, expectedMemberCounts, memberCounts)
	})

	t.Run("Returns users in group with timezones", func(t *testing.T) {
		memberCounts, _, _ := th.SystemAdminClient.GetChannelMemberCountsByGroup(channel.Id, true, "")
		expectedMemberCounts := []*model.ChannelMemberCountByGroup{
			{
				GroupId:                     th.Group.Id,
				ChannelMemberCount:          2,
				ChannelMemberTimezonesCount: 2,
			},
		}
		require.Equal(t, expectedMemberCounts, memberCounts)
	})

	id := model.NewId()
	group := &model.Group{
		DisplayName: "dn_" + id,
		Name:        model.NewString("name" + id),
		Source:      model.GroupSourceLdap,
		RemoteId:    model.NewString(model.NewId()),
	}

	_, appErr = th.App.CreateGroup(group)
	require.Nil(t, appErr)
	_, appErr = th.App.UpsertGroupMember(group.Id, user.Id)
	require.Nil(t, appErr)

	t.Run("Returns multiple groups with users in group with timezones", func(t *testing.T) {
		memberCounts, _, _ := th.SystemAdminClient.GetChannelMemberCountsByGroup(channel.Id, true, "")
		expectedMemberCounts := []*model.ChannelMemberCountByGroup{
			{
				GroupId:                     group.Id,
				ChannelMemberCount:          1,
				ChannelMemberTimezonesCount: 1,
			},
			{
				GroupId:                     th.Group.Id,
				ChannelMemberCount:          2,
				ChannelMemberTimezonesCount: 2,
			},
		}
		require.ElementsMatch(t, expectedMemberCounts, memberCounts)
	})
}

func TestMoveChannel(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()

	client := th.Client
	team1 := th.BasicTeam
	team2 := th.CreateTeam()

	t.Run("Should move channel", func(t *testing.T) {
		publicChannel := th.CreatePublicChannel()
		ch, _, err := th.SystemAdminClient.MoveChannel(publicChannel.Id, team2.Id, false)
		require.NoError(t, err)
		require.Equal(t, team2.Id, ch.TeamId)
	})

	t.Run("Should move private channel", func(t *testing.T) {
		channel := th.CreatePrivateChannel()
		ch, _, err := th.SystemAdminClient.MoveChannel(channel.Id, team1.Id, false)
		require.NoError(t, err)
		require.Equal(t, team1.Id, ch.TeamId)
	})

	t.Run("Should fail when trying to move a DM channel", func(t *testing.T) {
		user := th.CreateUser()
		dmChannel := th.CreateDmChannel(user)
		_, _, err := client.MoveChannel(dmChannel.Id, team1.Id, false)
		require.Error(t, err)
		CheckErrorID(t, err, "api.channel.move_channel.type.invalid")
	})

	t.Run("Should fail when trying to move a group channel", func(t *testing.T) {
		user := th.CreateUser()

		gmChannel, appErr := th.App.CreateGroupChannel(th.Context, []string{th.BasicUser.Id, th.SystemAdminUser.Id, th.TeamAdminUser.Id}, user.Id)
		require.Nil(t, appErr)
		_, _, err := client.MoveChannel(gmChannel.Id, team1.Id, false)
		require.Error(t, err)
		CheckErrorID(t, err, "api.channel.move_channel.type.invalid")
	})

	t.Run("Should fail due to permissions", func(t *testing.T) {
		publicChannel := th.CreatePublicChannel()
		_, _, err := client.MoveChannel(publicChannel.Id, team1.Id, false)
		require.Error(t, err)
		CheckErrorID(t, err, "api.context.permissions.app_error")
	})

	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
		publicChannel := th.CreatePublicChannel()
		user := th.BasicUser

		_, err := client.RemoveTeamMember(team2.Id, user.Id)
		require.NoError(t, err)

		_, _, err = client.AddChannelMember(publicChannel.Id, user.Id)
		require.NoError(t, err)

		_, _, err = client.MoveChannel(publicChannel.Id, team2.Id, false)
		require.Error(t, err)
		CheckErrorID(t, err, "app.channel.move_channel.members_do_not_match.error")
	}, "Should fail to move public channel due to a member not member of target team")

	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
		privateChannel := th.CreatePrivateChannel()
		user := th.BasicUser

		_, err := client.RemoveTeamMember(team2.Id, user.Id)
		require.NoError(t, err)

		_, _, err = client.AddChannelMember(privateChannel.Id, user.Id)
		require.NoError(t, err)

		_, _, err = client.MoveChannel(privateChannel.Id, team2.Id, false)
		require.Error(t, err)
		CheckErrorID(t, err, "app.channel.move_channel.members_do_not_match.error")
	}, "Should fail to move private channel due to a member not member of target team")

	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
		publicChannel := th.CreatePublicChannel()
		user := th.BasicUser

		_, err := client.RemoveTeamMember(team2.Id, user.Id)
		require.NoError(t, err)

		_, _, err = client.AddChannelMember(publicChannel.Id, user.Id)
		require.NoError(t, err)

		newChannel, _, err := client.MoveChannel(publicChannel.Id, team2.Id, true)
		require.NoError(t, err)
		require.Equal(t, team2.Id, newChannel.TeamId)
	}, "Should be able to (force) move public channel by a member that is not member of target team")

	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
		privateChannel := th.CreatePrivateChannel()
		user := th.BasicUser

		_, err := client.RemoveTeamMember(team2.Id, user.Id)
		require.NoError(t, err)

		_, _, err = client.AddChannelMember(privateChannel.Id, user.Id)
		require.NoError(t, err)

		newChannel, _, err := client.MoveChannel(privateChannel.Id, team2.Id, true)
		require.NoError(t, err)
		require.Equal(t, team2.Id, newChannel.TeamId)
	}, "Should be able to (force) move private channel by a member that is not member of target team")
}

func TestRootMentionsCount(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()

	client := th.Client
	user := th.BasicUser
	channel := th.BasicChannel

	// initially, MentionCountRoot is 0 in the database
	channelMember, err := th.App.Srv().Store().Channel().GetMember(context.Background(), channel.Id, user.Id)
	require.NoError(t, err)
	require.Equal(t, int64(0), channelMember.MentionCountRoot)
	require.Equal(t, int64(0), channelMember.MentionCount)

	// mention the user in a root post
	post1, _, err := th.SystemAdminClient.CreatePost(&model.Post{ChannelId: channel.Id, Message: "hey @" + user.Username})
	require.NoError(t, err)
	// mention the user in a reply post
	post2 := &model.Post{ChannelId: channel.Id, Message: "reply at @" + user.Username, RootId: post1.Id}
	_, _, err = th.SystemAdminClient.CreatePost(post2)
	require.NoError(t, err)

	// this should perform lazy migration and populate the field
	channelUnread, _, err := client.GetChannelUnread(channel.Id, user.Id)
	require.NoError(t, err)
	// reply post is not counted, so we should have one root mention
	require.EqualValues(t, int64(1), channelUnread.MentionCountRoot)
	// regular count stays the same
	require.Equal(t, int64(2), channelUnread.MentionCount)
	// validate that DB is updated
	channelMember, err = th.App.Srv().Store().Channel().GetMember(context.Background(), channel.Id, user.Id)
	require.NoError(t, err)
	require.EqualValues(t, int64(1), channelMember.MentionCountRoot)

	// validate that Team level counts are calculated
	counts, appErr := th.App.GetTeamUnread(channel.TeamId, user.Id)
	require.Nil(t, appErr)
	require.Equal(t, int64(1), counts.MentionCountRoot)
	require.Equal(t, int64(2), counts.MentionCount)
}

func TestViewChannelWithoutCollapsedThreads(t *testing.T) {
	th := Setup(t).InitBasic()
	defer th.TearDown()

	th.App.UpdateConfig(func(cfg *model.Config) {
		*cfg.ServiceSettings.ThreadAutoFollow = true
		*cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn
	})

	client := th.Client
	user := th.BasicUser
	team := th.BasicTeam
	channel := th.BasicChannel

	// mention the user in a root post
	post1, _, err := th.SystemAdminClient.CreatePost(&model.Post{ChannelId: channel.Id, Message: "hey @" + user.Username})
	require.NoError(t, err)
	// mention the user in a reply post
	post2 := &model.Post{ChannelId: channel.Id, Message: "reply at @" + user.Username, RootId: post1.Id}
	_, _, err = th.SystemAdminClient.CreatePost(post2)
	require.NoError(t, err)

	threads, _, err := client.GetUserThreads(user.Id, team.Id, model.GetUserThreadsOpts{})
	require.NoError(t, err)
	require.EqualValues(t, int64(1), threads.TotalUnreadMentions)

	// simulate opening the channel from an old client
	_, _, err = client.ViewChannel(user.Id, &model.ChannelView{
		ChannelId:                 channel.Id,
		PrevChannelId:             "",
		CollapsedThreadsSupported: false,
	})
	require.NoError(t, err)

	threads, _, err = client.GetUserThreads(user.Id, team.Id, model.GetUserThreadsOpts{})
	require.NoError(t, err)
	require.Zero(t, threads.TotalUnreadMentions)
}
