github.com/mad-app/mattermost-server@v5.11.1+incompatible/api4/post_test.go (about)

     1  // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package api4
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"net/http"
    10  	"net/http/httptest"
    11  	"net/url"
    12  	"reflect"
    13  	"strconv"
    14  	"strings"
    15  	"testing"
    16  	"time"
    17  
    18  	"github.com/stretchr/testify/assert"
    19  
    20  	"github.com/mattermost/mattermost-server/app"
    21  	"github.com/mattermost/mattermost-server/model"
    22  	"github.com/mattermost/mattermost-server/utils"
    23  	"github.com/mattermost/mattermost-server/utils/testutils"
    24  )
    25  
    26  func TestCreatePost(t *testing.T) {
    27  	th := Setup().InitBasic()
    28  	defer th.TearDown()
    29  	Client := th.Client
    30  
    31  	post := &model.Post{ChannelId: th.BasicChannel.Id, Message: "#hashtag a" + model.NewId() + "a", Props: model.StringInterface{model.PROPS_ADD_CHANNEL_MEMBER: "no good"}}
    32  	rpost, resp := Client.CreatePost(post)
    33  	CheckNoError(t, resp)
    34  	CheckCreatedStatus(t, resp)
    35  
    36  	if rpost.Message != post.Message {
    37  		t.Fatal("message didn't match")
    38  	}
    39  
    40  	if rpost.Hashtags != "#hashtag" {
    41  		t.Fatal("hashtag didn't match")
    42  	}
    43  
    44  	if len(rpost.FileIds) != 0 {
    45  		t.Fatal("shouldn't have files")
    46  	}
    47  
    48  	if rpost.EditAt != 0 {
    49  		t.Fatal("newly created post shouldn't have EditAt set")
    50  	}
    51  
    52  	if rpost.Props[model.PROPS_ADD_CHANNEL_MEMBER] != nil {
    53  		t.Fatal("newly created post shouldn't have Props['add_channel_member'] set")
    54  	}
    55  
    56  	post.RootId = rpost.Id
    57  	post.ParentId = rpost.Id
    58  	_, resp = Client.CreatePost(post)
    59  	CheckNoError(t, resp)
    60  
    61  	post.RootId = "junk"
    62  	_, resp = Client.CreatePost(post)
    63  	CheckBadRequestStatus(t, resp)
    64  
    65  	post.RootId = rpost.Id
    66  	post.ParentId = "junk"
    67  	_, resp = Client.CreatePost(post)
    68  	CheckBadRequestStatus(t, resp)
    69  
    70  	post2 := &model.Post{ChannelId: th.BasicChannel2.Id, Message: "zz" + model.NewId() + "a", CreateAt: 123}
    71  	rpost2, _ := Client.CreatePost(post2)
    72  
    73  	if rpost2.CreateAt == post2.CreateAt {
    74  		t.Fatal("create at should not match")
    75  	}
    76  
    77  	post.RootId = ""
    78  	post.ParentId = ""
    79  	post.Type = model.POST_SYSTEM_GENERIC
    80  	_, resp = Client.CreatePost(post)
    81  	CheckBadRequestStatus(t, resp)
    82  
    83  	post.Type = ""
    84  	post.RootId = rpost2.Id
    85  	post.ParentId = rpost2.Id
    86  	_, resp = Client.CreatePost(post)
    87  	CheckBadRequestStatus(t, resp)
    88  
    89  	post.RootId = ""
    90  	post.ParentId = ""
    91  	post.ChannelId = "junk"
    92  	_, resp = Client.CreatePost(post)
    93  	CheckForbiddenStatus(t, resp)
    94  
    95  	post.ChannelId = model.NewId()
    96  	_, resp = Client.CreatePost(post)
    97  	CheckForbiddenStatus(t, resp)
    98  
    99  	if r, err := Client.DoApiPost("/posts", "garbage"); err == nil {
   100  		t.Fatal("should have errored")
   101  	} else {
   102  		if r.StatusCode != http.StatusBadRequest {
   103  			t.Log("actual: " + strconv.Itoa(r.StatusCode))
   104  			t.Log("expected: " + strconv.Itoa(http.StatusBadRequest))
   105  			t.Fatal("wrong status code")
   106  		}
   107  	}
   108  
   109  	Client.Logout()
   110  	_, resp = Client.CreatePost(post)
   111  	CheckUnauthorizedStatus(t, resp)
   112  
   113  	post.ChannelId = th.BasicChannel.Id
   114  	post.CreateAt = 123
   115  	rpost, resp = th.SystemAdminClient.CreatePost(post)
   116  	CheckNoError(t, resp)
   117  
   118  	if rpost.CreateAt != post.CreateAt {
   119  		t.Fatal("create at should match")
   120  	}
   121  }
   122  
   123  func TestCreatePostEphemeral(t *testing.T) {
   124  	th := Setup().InitBasic()
   125  	defer th.TearDown()
   126  	Client := th.SystemAdminClient
   127  
   128  	ephemeralPost := &model.PostEphemeral{
   129  		UserID: th.BasicUser2.Id,
   130  		Post:   &model.Post{ChannelId: th.BasicChannel.Id, Message: "a" + model.NewId() + "a", Props: model.StringInterface{model.PROPS_ADD_CHANNEL_MEMBER: "no good"}},
   131  	}
   132  
   133  	rpost, resp := Client.CreatePostEphemeral(ephemeralPost)
   134  	CheckNoError(t, resp)
   135  	CheckCreatedStatus(t, resp)
   136  
   137  	if rpost.Message != ephemeralPost.Post.Message {
   138  		t.Fatal("message didn't match")
   139  	}
   140  
   141  	if rpost.EditAt != 0 {
   142  		t.Fatal("newly created ephemeral post shouldn't have EditAt set")
   143  	}
   144  
   145  	if r, err := Client.DoApiPost("/posts/ephemeral", "garbage"); err == nil {
   146  		t.Fatal("should have errored")
   147  	} else {
   148  		if r.StatusCode != http.StatusBadRequest {
   149  			t.Log("actual: " + strconv.Itoa(r.StatusCode))
   150  			t.Log("expected: " + strconv.Itoa(http.StatusBadRequest))
   151  			t.Fatal("wrong status code")
   152  		}
   153  	}
   154  
   155  	Client.Logout()
   156  	_, resp = Client.CreatePostEphemeral(ephemeralPost)
   157  	CheckUnauthorizedStatus(t, resp)
   158  
   159  	Client = th.Client
   160  	_, resp = Client.CreatePostEphemeral(ephemeralPost)
   161  	CheckForbiddenStatus(t, resp)
   162  }
   163  
   164  func testCreatePostWithOutgoingHook(
   165  	t *testing.T,
   166  	hookContentType, expectedContentType, message, triggerWord string,
   167  	fileIds []string,
   168  	triggerWhen int,
   169  	commentPostType bool,
   170  ) {
   171  	th := Setup().InitBasic()
   172  	defer th.TearDown()
   173  	user := th.SystemAdminUser
   174  	team := th.BasicTeam
   175  	channel := th.BasicChannel
   176  
   177  	enableOutgoingWebhooks := *th.App.Config().ServiceSettings.EnableOutgoingWebhooks
   178  	allowedUntrustedInternalConnections := *th.App.Config().ServiceSettings.AllowedUntrustedInternalConnections
   179  	defer func() {
   180  		th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingWebhooks })
   181  		th.App.UpdateConfig(func(cfg *model.Config) {
   182  			*cfg.ServiceSettings.AllowedUntrustedInternalConnections = allowedUntrustedInternalConnections
   183  		})
   184  	}()
   185  
   186  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOutgoingWebhooks = true })
   187  	th.App.UpdateConfig(func(cfg *model.Config) {
   188  		*cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost 127.0.0.1"
   189  	})
   190  
   191  	var hook *model.OutgoingWebhook
   192  	var post *model.Post
   193  
   194  	// Create a test server that is the target of the outgoing webhook. It will
   195  	// validate the webhook body fields and write to the success channel on
   196  	// success/failure.
   197  	success := make(chan bool)
   198  	wait := make(chan bool, 1)
   199  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   200  		<-wait
   201  
   202  		requestContentType := r.Header.Get("Content-Type")
   203  		if requestContentType != expectedContentType {
   204  			t.Logf("Content-Type is %s, should be %s", requestContentType, expectedContentType)
   205  			success <- false
   206  			return
   207  		}
   208  
   209  		expectedPayload := &model.OutgoingWebhookPayload{
   210  			Token:       hook.Token,
   211  			TeamId:      hook.TeamId,
   212  			TeamDomain:  team.Name,
   213  			ChannelId:   post.ChannelId,
   214  			ChannelName: channel.Name,
   215  			Timestamp:   post.CreateAt,
   216  			UserId:      post.UserId,
   217  			UserName:    user.Username,
   218  			PostId:      post.Id,
   219  			Text:        post.Message,
   220  			TriggerWord: triggerWord,
   221  			FileIds:     strings.Join(post.FileIds, ","),
   222  		}
   223  
   224  		// depending on the Content-Type, we expect to find a JSON or form encoded payload
   225  		if requestContentType == "application/json" {
   226  			decoder := json.NewDecoder(r.Body)
   227  			o := &model.OutgoingWebhookPayload{}
   228  			decoder.Decode(&o)
   229  
   230  			if !reflect.DeepEqual(expectedPayload, o) {
   231  				t.Logf("JSON payload is %+v, should be %+v", o, expectedPayload)
   232  				success <- false
   233  				return
   234  			}
   235  		} else {
   236  			err := r.ParseForm()
   237  			if err != nil {
   238  				t.Logf("Error parsing form: %q", err)
   239  				success <- false
   240  				return
   241  			}
   242  
   243  			expectedFormValues, _ := url.ParseQuery(expectedPayload.ToFormValues())
   244  
   245  			if !reflect.DeepEqual(expectedFormValues, r.Form) {
   246  				t.Logf("Form values are: %q\n, should be: %q\n", r.Form, expectedFormValues)
   247  				success <- false
   248  				return
   249  			}
   250  		}
   251  
   252  		respPostType := "" //if is empty or post will do a normal post.
   253  		if commentPostType {
   254  			respPostType = model.OUTGOING_HOOK_RESPONSE_TYPE_COMMENT
   255  		}
   256  
   257  		outGoingHookResponse := &model.OutgoingWebhookResponse{
   258  			Text:         model.NewString("some test text"),
   259  			Username:     "TestCommandServer",
   260  			IconURL:      "https://www.mattermost.org/wp-content/uploads/2016/04/icon.png",
   261  			Type:         "custom_as",
   262  			ResponseType: respPostType,
   263  		}
   264  
   265  		fmt.Fprintf(w, outGoingHookResponse.ToJson())
   266  		success <- true
   267  	}))
   268  	defer ts.Close()
   269  
   270  	// create an outgoing webhook, passing it the test server URL
   271  	var triggerWords []string
   272  	if triggerWord != "" {
   273  		triggerWords = []string{triggerWord}
   274  	}
   275  
   276  	hook = &model.OutgoingWebhook{
   277  		ChannelId:    channel.Id,
   278  		TeamId:       team.Id,
   279  		ContentType:  hookContentType,
   280  		TriggerWords: triggerWords,
   281  		TriggerWhen:  triggerWhen,
   282  		CallbackURLs: []string{ts.URL},
   283  	}
   284  
   285  	hook, resp := th.SystemAdminClient.CreateOutgoingWebhook(hook)
   286  	CheckNoError(t, resp)
   287  
   288  	// create a post to trigger the webhook
   289  	post = &model.Post{
   290  		ChannelId: channel.Id,
   291  		Message:   message,
   292  		FileIds:   fileIds,
   293  	}
   294  
   295  	post, resp = th.SystemAdminClient.CreatePost(post)
   296  	CheckNoError(t, resp)
   297  
   298  	wait <- true
   299  
   300  	// We wait for the test server to write to the success channel and we make
   301  	// the test fail if that doesn't happen before the timeout.
   302  	select {
   303  	case ok := <-success:
   304  		if !ok {
   305  			t.Fatal("Test server did send an invalid webhook.")
   306  		}
   307  	case <-time.After(time.Second):
   308  		t.Fatal("Timeout, test server did not send the webhook.")
   309  	}
   310  
   311  	if commentPostType {
   312  		time.Sleep(time.Millisecond * 100)
   313  		postList, resp := th.SystemAdminClient.GetPostThread(post.Id, "")
   314  		CheckNoError(t, resp)
   315  		if postList.Order[0] != post.Id {
   316  			t.Fatal("wrong order")
   317  		}
   318  
   319  		if _, ok := postList.Posts[post.Id]; !ok {
   320  			t.Fatal("should have had post")
   321  		}
   322  
   323  		if len(postList.Posts) != 2 {
   324  			t.Fatal("should have 2 posts")
   325  		}
   326  
   327  	}
   328  }
   329  
   330  func TestCreatePostWithOutgoingHook_form_urlencoded(t *testing.T) {
   331  	testCreatePostWithOutgoingHook(t, "application/x-www-form-urlencoded", "application/x-www-form-urlencoded", "triggerword lorem ipsum", "triggerword", []string{"file_id_1"}, app.TRIGGERWORDS_EXACT_MATCH, false)
   332  	testCreatePostWithOutgoingHook(t, "application/x-www-form-urlencoded", "application/x-www-form-urlencoded", "triggerwordaaazzz lorem ipsum", "triggerword", []string{"file_id_1"}, app.TRIGGERWORDS_STARTS_WITH, false)
   333  	testCreatePostWithOutgoingHook(t, "application/x-www-form-urlencoded", "application/x-www-form-urlencoded", "", "", []string{"file_id_1"}, app.TRIGGERWORDS_EXACT_MATCH, false)
   334  	testCreatePostWithOutgoingHook(t, "application/x-www-form-urlencoded", "application/x-www-form-urlencoded", "", "", []string{"file_id_1"}, app.TRIGGERWORDS_STARTS_WITH, false)
   335  	testCreatePostWithOutgoingHook(t, "application/x-www-form-urlencoded", "application/x-www-form-urlencoded", "triggerword lorem ipsum", "triggerword", []string{"file_id_1"}, app.TRIGGERWORDS_EXACT_MATCH, true)
   336  	testCreatePostWithOutgoingHook(t, "application/x-www-form-urlencoded", "application/x-www-form-urlencoded", "triggerwordaaazzz lorem ipsum", "triggerword", []string{"file_id_1"}, app.TRIGGERWORDS_STARTS_WITH, true)
   337  }
   338  
   339  func TestCreatePostWithOutgoingHook_json(t *testing.T) {
   340  	testCreatePostWithOutgoingHook(t, "application/json", "application/json", "triggerword lorem ipsum", "triggerword", []string{"file_id_1, file_id_2"}, app.TRIGGERWORDS_EXACT_MATCH, false)
   341  	testCreatePostWithOutgoingHook(t, "application/json", "application/json", "triggerwordaaazzz lorem ipsum", "triggerword", []string{"file_id_1, file_id_2"}, app.TRIGGERWORDS_STARTS_WITH, false)
   342  	testCreatePostWithOutgoingHook(t, "application/json", "application/json", "triggerword lorem ipsum", "", []string{"file_id_1"}, app.TRIGGERWORDS_EXACT_MATCH, false)
   343  	testCreatePostWithOutgoingHook(t, "application/json", "application/json", "triggerwordaaazzz lorem ipsum", "", []string{"file_id_1"}, app.TRIGGERWORDS_STARTS_WITH, false)
   344  	testCreatePostWithOutgoingHook(t, "application/json", "application/json", "triggerword lorem ipsum", "triggerword", []string{"file_id_1, file_id_2"}, app.TRIGGERWORDS_EXACT_MATCH, true)
   345  	testCreatePostWithOutgoingHook(t, "application/json", "application/json", "triggerwordaaazzz lorem ipsum", "", []string{"file_id_1"}, app.TRIGGERWORDS_STARTS_WITH, true)
   346  }
   347  
   348  // hooks created before we added the ContentType field should be considered as
   349  // application/x-www-form-urlencoded
   350  func TestCreatePostWithOutgoingHook_no_content_type(t *testing.T) {
   351  	testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded", "triggerword lorem ipsum", "triggerword", []string{"file_id_1"}, app.TRIGGERWORDS_EXACT_MATCH, false)
   352  	testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded", "triggerwordaaazzz lorem ipsum", "triggerword", []string{"file_id_1"}, app.TRIGGERWORDS_STARTS_WITH, false)
   353  	testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded", "triggerword lorem ipsum", "", []string{"file_id_1, file_id_2"}, app.TRIGGERWORDS_EXACT_MATCH, false)
   354  	testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded", "triggerwordaaazzz lorem ipsum", "", []string{"file_id_1, file_id_2"}, app.TRIGGERWORDS_STARTS_WITH, false)
   355  	testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded", "triggerword lorem ipsum", "triggerword", []string{"file_id_1"}, app.TRIGGERWORDS_EXACT_MATCH, true)
   356  	testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded", "triggerword lorem ipsum", "", []string{"file_id_1, file_id_2"}, app.TRIGGERWORDS_EXACT_MATCH, true)
   357  }
   358  
   359  func TestCreatePostPublic(t *testing.T) {
   360  	th := Setup().InitBasic()
   361  	defer th.TearDown()
   362  	Client := th.Client
   363  
   364  	post := &model.Post{ChannelId: th.BasicChannel.Id, Message: "#hashtag a" + model.NewId() + "a"}
   365  
   366  	user := model.User{Email: th.GenerateTestEmail(), Nickname: "Joram Wilander", Password: "hello1", Username: GenerateTestUsername(), Roles: model.SYSTEM_USER_ROLE_ID}
   367  
   368  	ruser, resp := Client.CreateUser(&user)
   369  	CheckNoError(t, resp)
   370  
   371  	Client.Login(user.Email, user.Password)
   372  
   373  	_, resp = Client.CreatePost(post)
   374  	CheckForbiddenStatus(t, resp)
   375  
   376  	th.App.UpdateUserRoles(ruser.Id, model.SYSTEM_USER_ROLE_ID+" "+model.SYSTEM_POST_ALL_PUBLIC_ROLE_ID, false)
   377  	th.App.InvalidateAllCaches()
   378  
   379  	Client.Login(user.Email, user.Password)
   380  
   381  	_, resp = Client.CreatePost(post)
   382  	CheckNoError(t, resp)
   383  
   384  	post.ChannelId = th.BasicPrivateChannel.Id
   385  	_, resp = Client.CreatePost(post)
   386  	CheckForbiddenStatus(t, resp)
   387  
   388  	th.App.UpdateUserRoles(ruser.Id, model.SYSTEM_USER_ROLE_ID, false)
   389  	th.App.JoinUserToTeam(th.BasicTeam, ruser, "")
   390  	th.App.UpdateTeamMemberRoles(th.BasicTeam.Id, ruser.Id, model.TEAM_USER_ROLE_ID+" "+model.TEAM_POST_ALL_PUBLIC_ROLE_ID)
   391  	th.App.InvalidateAllCaches()
   392  
   393  	Client.Login(user.Email, user.Password)
   394  
   395  	post.ChannelId = th.BasicPrivateChannel.Id
   396  	_, resp = Client.CreatePost(post)
   397  	CheckForbiddenStatus(t, resp)
   398  
   399  	post.ChannelId = th.BasicChannel.Id
   400  	_, resp = Client.CreatePost(post)
   401  	CheckNoError(t, resp)
   402  }
   403  
   404  func TestCreatePostAll(t *testing.T) {
   405  	th := Setup().InitBasic()
   406  	defer th.TearDown()
   407  	Client := th.Client
   408  
   409  	post := &model.Post{ChannelId: th.BasicChannel.Id, Message: "#hashtag a" + model.NewId() + "a"}
   410  
   411  	user := model.User{Email: th.GenerateTestEmail(), Nickname: "Joram Wilander", Password: "hello1", Username: GenerateTestUsername(), Roles: model.SYSTEM_USER_ROLE_ID}
   412  
   413  	directChannel, _ := th.App.GetOrCreateDirectChannel(th.BasicUser.Id, th.BasicUser2.Id)
   414  
   415  	ruser, resp := Client.CreateUser(&user)
   416  	CheckNoError(t, resp)
   417  
   418  	Client.Login(user.Email, user.Password)
   419  
   420  	_, resp = Client.CreatePost(post)
   421  	CheckForbiddenStatus(t, resp)
   422  
   423  	th.App.UpdateUserRoles(ruser.Id, model.SYSTEM_USER_ROLE_ID+" "+model.SYSTEM_POST_ALL_ROLE_ID, false)
   424  	th.App.InvalidateAllCaches()
   425  
   426  	Client.Login(user.Email, user.Password)
   427  
   428  	_, resp = Client.CreatePost(post)
   429  	CheckNoError(t, resp)
   430  
   431  	post.ChannelId = th.BasicPrivateChannel.Id
   432  	_, resp = Client.CreatePost(post)
   433  	CheckNoError(t, resp)
   434  
   435  	post.ChannelId = directChannel.Id
   436  	_, resp = Client.CreatePost(post)
   437  	CheckNoError(t, resp)
   438  
   439  	th.App.UpdateUserRoles(ruser.Id, model.SYSTEM_USER_ROLE_ID, false)
   440  	th.App.JoinUserToTeam(th.BasicTeam, ruser, "")
   441  	th.App.UpdateTeamMemberRoles(th.BasicTeam.Id, ruser.Id, model.TEAM_USER_ROLE_ID+" "+model.TEAM_POST_ALL_ROLE_ID)
   442  	th.App.InvalidateAllCaches()
   443  
   444  	Client.Login(user.Email, user.Password)
   445  
   446  	post.ChannelId = th.BasicPrivateChannel.Id
   447  	_, resp = Client.CreatePost(post)
   448  	CheckNoError(t, resp)
   449  
   450  	post.ChannelId = th.BasicChannel.Id
   451  	_, resp = Client.CreatePost(post)
   452  	CheckNoError(t, resp)
   453  
   454  	post.ChannelId = directChannel.Id
   455  	_, resp = Client.CreatePost(post)
   456  	CheckForbiddenStatus(t, resp)
   457  }
   458  
   459  func TestCreatePostSendOutOfChannelMentions(t *testing.T) {
   460  	th := Setup().InitBasic()
   461  	defer th.TearDown()
   462  	Client := th.Client
   463  
   464  	WebSocketClient, err := th.CreateWebSocketClient()
   465  	if err != nil {
   466  		t.Fatal(err)
   467  	}
   468  	WebSocketClient.Listen()
   469  
   470  	inChannelUser := th.CreateUser()
   471  	th.LinkUserToTeam(inChannelUser, th.BasicTeam)
   472  	th.App.AddUserToChannel(inChannelUser, th.BasicChannel)
   473  
   474  	post1 := &model.Post{ChannelId: th.BasicChannel.Id, Message: "@" + inChannelUser.Username}
   475  	_, resp := Client.CreatePost(post1)
   476  	CheckNoError(t, resp)
   477  	CheckCreatedStatus(t, resp)
   478  
   479  	timeout := time.After(300 * time.Millisecond)
   480  	waiting := true
   481  	for waiting {
   482  		select {
   483  		case event := <-WebSocketClient.EventChannel:
   484  			if event.Event == model.WEBSOCKET_EVENT_EPHEMERAL_MESSAGE {
   485  				t.Fatal("should not have ephemeral message event")
   486  			}
   487  
   488  		case <-timeout:
   489  			waiting = false
   490  		}
   491  	}
   492  
   493  	outOfChannelUser := th.CreateUser()
   494  	th.LinkUserToTeam(outOfChannelUser, th.BasicTeam)
   495  
   496  	post2 := &model.Post{ChannelId: th.BasicChannel.Id, Message: "@" + outOfChannelUser.Username}
   497  	_, resp = Client.CreatePost(post2)
   498  	CheckNoError(t, resp)
   499  	CheckCreatedStatus(t, resp)
   500  
   501  	timeout = time.After(300 * time.Millisecond)
   502  	waiting = true
   503  	for waiting {
   504  		select {
   505  		case event := <-WebSocketClient.EventChannel:
   506  			if event.Event != model.WEBSOCKET_EVENT_EPHEMERAL_MESSAGE {
   507  				// Ignore any other events
   508  				continue
   509  			}
   510  
   511  			wpost := model.PostFromJson(strings.NewReader(event.Data["post"].(string)))
   512  			if acm, ok := wpost.Props[model.PROPS_ADD_CHANNEL_MEMBER].(map[string]interface{}); !ok {
   513  				t.Fatal("should have received ephemeral post with 'add_channel_member' in props")
   514  			} else {
   515  				if acm["post_id"] == nil || acm["user_ids"] == nil || acm["usernames"] == nil {
   516  					t.Fatal("should not be nil")
   517  				}
   518  			}
   519  			waiting = false
   520  		case <-timeout:
   521  			t.Fatal("timed out waiting for ephemeral message event")
   522  		}
   523  	}
   524  }
   525  
   526  func TestUpdatePost(t *testing.T) {
   527  	th := Setup().InitBasic()
   528  	defer th.TearDown()
   529  	Client := th.Client
   530  	channel := th.BasicChannel
   531  
   532  	th.App.SetLicense(model.NewTestLicense())
   533  
   534  	post := &model.Post{ChannelId: channel.Id, Message: "zz" + model.NewId() + "a"}
   535  	rpost, resp := Client.CreatePost(post)
   536  	CheckNoError(t, resp)
   537  
   538  	if rpost.Message != post.Message {
   539  		t.Fatal("full name didn't match")
   540  	}
   541  
   542  	if rpost.EditAt != 0 {
   543  		t.Fatal("Newly created post shouldn't have EditAt set")
   544  	}
   545  
   546  	msg := "zz" + model.NewId() + " update post"
   547  	rpost.Message = msg
   548  	rpost.UserId = ""
   549  
   550  	rupost, resp := Client.UpdatePost(rpost.Id, rpost)
   551  	CheckNoError(t, resp)
   552  
   553  	if rupost.Message != msg {
   554  		t.Fatal("failed to updates")
   555  	}
   556  	if rupost.EditAt == 0 {
   557  		t.Fatal("EditAt not updated for post")
   558  	}
   559  
   560  	msg1 := "#hashtag a" + model.NewId() + " update post again"
   561  	rpost.Message = msg1
   562  	rpost.Props[model.PROPS_ADD_CHANNEL_MEMBER] = "no good"
   563  	rrupost, resp := Client.UpdatePost(rpost.Id, rpost)
   564  	CheckNoError(t, resp)
   565  
   566  	if rrupost.Message != msg1 && rrupost.Hashtags != "#hashtag" {
   567  		t.Fatal("failed to updates")
   568  	}
   569  
   570  	if rrupost.Props[model.PROPS_ADD_CHANNEL_MEMBER] != nil {
   571  		t.Fatal("failed to sanitize Props['add_channel_member'], should be nil")
   572  	}
   573  
   574  	rpost2, err := th.App.CreatePost(&model.Post{ChannelId: channel.Id, Message: "zz" + model.NewId() + "a", Type: model.POST_JOIN_LEAVE, UserId: th.BasicUser.Id}, channel, false)
   575  	if err != nil {
   576  		t.Fatal(err)
   577  	}
   578  
   579  	up2 := &model.Post{Id: rpost2.Id, ChannelId: channel.Id, Message: "zz" + model.NewId() + " update post 2"}
   580  	_, resp = Client.UpdatePost(rpost2.Id, up2)
   581  	CheckBadRequestStatus(t, resp)
   582  
   583  	rpost3, err := th.App.CreatePost(&model.Post{ChannelId: channel.Id, Message: "zz" + model.NewId() + "a", UserId: th.BasicUser.Id}, channel, false)
   584  	if err != nil {
   585  		t.Fatal(err)
   586  	}
   587  
   588  	fileIds := model.StringArray{"abcdef", "geh"}
   589  	up3 := &model.Post{Id: rpost3.Id, ChannelId: channel.Id, Message: "zz" + model.NewId() + " update post 3", FileIds: fileIds}
   590  	rrupost3, resp := Client.UpdatePost(rpost3.Id, up3)
   591  	CheckNoError(t, resp)
   592  	assert.Empty(t, rrupost3.FileIds)
   593  
   594  	up4 := &model.Post{Id: rpost3.Id, ChannelId: channel.Id, Message: "zz" + model.NewId() + " update post 3"}
   595  	up4.AddProp("attachments", []model.SlackAttachment{
   596  		{
   597  			Text: "Hello World",
   598  		},
   599  	})
   600  	rrupost3, resp = Client.UpdatePost(rpost3.Id, up4)
   601  	CheckNoError(t, resp)
   602  	assert.NotEqual(t, rpost3.EditAt, rrupost3.EditAt)
   603  	assert.NotEqual(t, rpost3.Attachments(), rrupost3.Attachments())
   604  
   605  
   606  	Client.Logout()
   607  	_, resp = Client.UpdatePost(rpost.Id, rpost)
   608  	CheckUnauthorizedStatus(t, resp)
   609  
   610  	th.LoginBasic2()
   611  	_, resp = Client.UpdatePost(rpost.Id, rpost)
   612  	CheckForbiddenStatus(t, resp)
   613  
   614  	Client.Logout()
   615  
   616  	th.LoginTeamAdmin()
   617  	_, resp = Client.UpdatePost(rpost.Id, rpost)
   618  	CheckForbiddenStatus(t, resp)
   619  
   620  	Client.Logout()
   621  
   622  	_, resp = th.SystemAdminClient.UpdatePost(rpost.Id, rpost)
   623  	CheckNoError(t, resp)
   624  }
   625  
   626  func TestUpdateOthersPostInDirectMessageChannel(t *testing.T) {
   627  	// This test checks that a sysadmin with the "EDIT_OTHERS_POSTS" permission can edit someone else's post in a
   628  	// channel without a team (DM/GM). This indirectly checks for the proper cascading all the way to system-wide roles
   629  	// on the user object of permissions based on a post in a channel with no team ID.
   630  	th := Setup().InitBasic()
   631  	defer th.TearDown()
   632  
   633  	dmChannel := th.CreateDmChannel(th.SystemAdminUser)
   634  
   635  	post := &model.Post{
   636  		Message:       "asd",
   637  		ChannelId:     dmChannel.Id,
   638  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   639  		UserId:        th.BasicUser.Id,
   640  		CreateAt:      0,
   641  	}
   642  
   643  	post, resp := th.Client.CreatePost(post)
   644  	CheckNoError(t, resp)
   645  
   646  	post.Message = "changed"
   647  	post, resp = th.SystemAdminClient.UpdatePost(post.Id, post)
   648  	CheckNoError(t, resp)
   649  }
   650  
   651  func TestPatchPost(t *testing.T) {
   652  	th := Setup().InitBasic()
   653  	defer th.TearDown()
   654  	Client := th.Client
   655  	channel := th.BasicChannel
   656  
   657  	th.App.SetLicense(model.NewTestLicense())
   658  
   659  	post := &model.Post{
   660  		ChannelId:    channel.Id,
   661  		IsPinned:     true,
   662  		Message:      "#hashtag a message",
   663  		Props:        model.StringInterface{"channel_header": "old_header"},
   664  		FileIds:      model.StringArray{"file1", "file2"},
   665  		HasReactions: true,
   666  	}
   667  	post, _ = Client.CreatePost(post)
   668  
   669  	patch := &model.PostPatch{}
   670  
   671  	patch.IsPinned = model.NewBool(false)
   672  	patch.Message = model.NewString("#otherhashtag other message")
   673  	patch.Props = new(model.StringInterface)
   674  	*patch.Props = model.StringInterface{"channel_header": "new_header"}
   675  	patch.FileIds = new(model.StringArray)
   676  	*patch.FileIds = model.StringArray{"file1", "otherfile2", "otherfile3"}
   677  	patch.HasReactions = model.NewBool(false)
   678  
   679  	rpost, resp := Client.PatchPost(post.Id, patch)
   680  	CheckNoError(t, resp)
   681  
   682  	if rpost.IsPinned {
   683  		t.Fatal("IsPinned did not update properly")
   684  	}
   685  	if rpost.Message != "#otherhashtag other message" {
   686  		t.Fatal("Message did not update properly")
   687  	}
   688  	if len(rpost.Props) != 1 {
   689  		t.Fatal("Props did not update properly")
   690  	}
   691  	if !reflect.DeepEqual(rpost.Props, *patch.Props) {
   692  		t.Fatal("Props did not update properly")
   693  	}
   694  	if rpost.Hashtags != "#otherhashtag" {
   695  		t.Fatal("Message did not update properly")
   696  	}
   697  	if len(rpost.FileIds) == 3 {
   698  		t.Fatal("FileIds should not update properly")
   699  	}
   700  	if reflect.DeepEqual(rpost.FileIds, *patch.FileIds) {
   701  		t.Fatal("FileIds should not update")
   702  	}
   703  	if rpost.HasReactions {
   704  		t.Fatal("HasReactions did not update properly")
   705  	}
   706  
   707  	patch2 := &model.PostPatch{}
   708  	attachments := []model.SlackAttachment{
   709  		{
   710  			Text: "Hello World",
   711  		},
   712  	}
   713  	patch2.Props = new(model.StringInterface)
   714  	*patch2.Props = model.StringInterface{"attachments": attachments}
   715  
   716  	rpost2, resp := Client.PatchPost(post.Id, patch2)
   717  	CheckNoError(t, resp)
   718  	assert.NotEmpty(t, rpost2.Props["attachments"])
   719  	assert.NotEqual(t, rpost.EditAt, rpost2.EditAt)
   720  
   721  	if r, err := Client.DoApiPut("/posts/"+post.Id+"/patch", "garbage"); err == nil {
   722  		t.Fatal("should have errored")
   723  	} else {
   724  		if r.StatusCode != http.StatusBadRequest {
   725  			t.Log("actual: " + strconv.Itoa(r.StatusCode))
   726  			t.Log("expected: " + strconv.Itoa(http.StatusBadRequest))
   727  			t.Fatal("wrong status code")
   728  		}
   729  	}
   730  
   731  	_, resp = Client.PatchPost("junk", patch)
   732  	CheckBadRequestStatus(t, resp)
   733  
   734  	_, resp = Client.PatchPost(GenerateTestId(), patch)
   735  	CheckForbiddenStatus(t, resp)
   736  
   737  	Client.Logout()
   738  	_, resp = Client.PatchPost(post.Id, patch)
   739  	CheckUnauthorizedStatus(t, resp)
   740  
   741  	th.LoginBasic2()
   742  	_, resp = Client.PatchPost(post.Id, patch)
   743  	CheckForbiddenStatus(t, resp)
   744  
   745  	th.LoginTeamAdmin()
   746  	_, resp = Client.PatchPost(post.Id, patch)
   747  	CheckForbiddenStatus(t, resp)
   748  
   749  	_, resp = th.SystemAdminClient.PatchPost(post.Id, patch)
   750  	CheckNoError(t, resp)
   751  }
   752  
   753  func TestPinPost(t *testing.T) {
   754  	th := Setup().InitBasic()
   755  	defer th.TearDown()
   756  	Client := th.Client
   757  
   758  	post := th.BasicPost
   759  	pass, resp := Client.PinPost(post.Id)
   760  	CheckNoError(t, resp)
   761  
   762  	if !pass {
   763  		t.Fatal("should have passed")
   764  	}
   765  
   766  	if rpost, err := th.App.GetSinglePost(post.Id); err != nil && !rpost.IsPinned {
   767  		t.Fatal("failed to pin post")
   768  	}
   769  
   770  	pass, resp = Client.PinPost("junk")
   771  	CheckBadRequestStatus(t, resp)
   772  
   773  	if pass {
   774  		t.Fatal("should have failed")
   775  	}
   776  
   777  	_, resp = Client.PinPost(GenerateTestId())
   778  	CheckForbiddenStatus(t, resp)
   779  
   780  	t.Run("unable-to-pin-post-in-read-only-town-square", func(t *testing.T) {
   781  		townSquareIsReadOnly := *th.App.Config().TeamSettings.ExperimentalTownSquareIsReadOnly
   782  		th.App.SetLicense(model.NewTestLicense())
   783  		th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.ExperimentalTownSquareIsReadOnly = true })
   784  
   785  		defer th.App.RemoveLicense()
   786  		defer th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.ExperimentalTownSquareIsReadOnly = townSquareIsReadOnly })
   787  
   788  		channel, err := th.App.GetChannelByName("town-square", th.BasicTeam.Id, true)
   789  		assert.Nil(t, err)
   790  		adminPost := th.CreatePostWithClient(th.SystemAdminClient, channel)
   791  
   792  		_, resp = Client.PinPost(adminPost.Id)
   793  		CheckForbiddenStatus(t, resp)
   794  	})
   795  
   796  	Client.Logout()
   797  	_, resp = Client.PinPost(post.Id)
   798  	CheckUnauthorizedStatus(t, resp)
   799  
   800  	_, resp = th.SystemAdminClient.PinPost(post.Id)
   801  	CheckNoError(t, resp)
   802  }
   803  
   804  func TestUnpinPost(t *testing.T) {
   805  	th := Setup().InitBasic()
   806  	defer th.TearDown()
   807  	Client := th.Client
   808  
   809  	pinnedPost := th.CreatePinnedPost()
   810  	pass, resp := Client.UnpinPost(pinnedPost.Id)
   811  	CheckNoError(t, resp)
   812  
   813  	if !pass {
   814  		t.Fatal("should have passed")
   815  	}
   816  
   817  	if rpost, err := th.App.GetSinglePost(pinnedPost.Id); err != nil && rpost.IsPinned {
   818  		t.Fatal("failed to pin post")
   819  	}
   820  
   821  	pass, resp = Client.UnpinPost("junk")
   822  	CheckBadRequestStatus(t, resp)
   823  
   824  	if pass {
   825  		t.Fatal("should have failed")
   826  	}
   827  
   828  	_, resp = Client.UnpinPost(GenerateTestId())
   829  	CheckForbiddenStatus(t, resp)
   830  
   831  	Client.Logout()
   832  	_, resp = Client.UnpinPost(pinnedPost.Id)
   833  	CheckUnauthorizedStatus(t, resp)
   834  
   835  	_, resp = th.SystemAdminClient.UnpinPost(pinnedPost.Id)
   836  	CheckNoError(t, resp)
   837  }
   838  
   839  func TestGetPostsForChannel(t *testing.T) {
   840  	th := Setup().InitBasic()
   841  	defer th.TearDown()
   842  	Client := th.Client
   843  
   844  	post1 := th.CreatePost()
   845  	post2 := th.CreatePost()
   846  	post3 := &model.Post{ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a", RootId: post1.Id}
   847  	post3, _ = Client.CreatePost(post3)
   848  
   849  	time.Sleep(300 * time.Millisecond)
   850  	since := model.GetMillis()
   851  	time.Sleep(300 * time.Millisecond)
   852  
   853  	post4 := th.CreatePost()
   854  
   855  	posts, resp := Client.GetPostsForChannel(th.BasicChannel.Id, 0, 60, "")
   856  	CheckNoError(t, resp)
   857  
   858  	if posts.Order[0] != post4.Id {
   859  		t.Fatal("wrong order")
   860  	}
   861  
   862  	if posts.Order[1] != post3.Id {
   863  		t.Fatal("wrong order")
   864  	}
   865  
   866  	if posts.Order[2] != post2.Id {
   867  		t.Fatal("wrong order")
   868  	}
   869  
   870  	if posts.Order[3] != post1.Id {
   871  		t.Fatal("wrong order")
   872  	}
   873  
   874  	posts, resp = Client.GetPostsForChannel(th.BasicChannel.Id, 0, 3, resp.Etag)
   875  	CheckEtag(t, posts, resp)
   876  
   877  	posts, resp = Client.GetPostsForChannel(th.BasicChannel.Id, 0, 3, "")
   878  	CheckNoError(t, resp)
   879  
   880  	if len(posts.Order) != 3 {
   881  		t.Fatal("wrong number returned")
   882  	}
   883  
   884  	if _, ok := posts.Posts[post3.Id]; !ok {
   885  		t.Fatal("missing comment")
   886  	}
   887  
   888  	if _, ok := posts.Posts[post1.Id]; !ok {
   889  		t.Fatal("missing root post")
   890  	}
   891  
   892  	posts, resp = Client.GetPostsForChannel(th.BasicChannel.Id, 1, 1, "")
   893  	CheckNoError(t, resp)
   894  
   895  	if posts.Order[0] != post3.Id {
   896  		t.Fatal("wrong order")
   897  	}
   898  
   899  	posts, resp = Client.GetPostsForChannel(th.BasicChannel.Id, 10000, 10000, "")
   900  	CheckNoError(t, resp)
   901  
   902  	if len(posts.Order) != 0 {
   903  		t.Fatal("should be no posts")
   904  	}
   905  
   906  	post5 := th.CreatePost()
   907  
   908  	posts, resp = Client.GetPostsSince(th.BasicChannel.Id, since)
   909  	CheckNoError(t, resp)
   910  
   911  	if len(posts.Posts) != 2 {
   912  		t.Log(posts.Posts)
   913  		t.Fatal("should return 2 posts")
   914  	}
   915  
   916  	found := make([]bool, 2)
   917  	for _, p := range posts.Posts {
   918  		if p.CreateAt < since {
   919  			t.Fatal("bad create at for post returned")
   920  		}
   921  		if p.Id == post4.Id {
   922  			found[0] = true
   923  		} else if p.Id == post5.Id {
   924  			found[1] = true
   925  		}
   926  	}
   927  
   928  	for _, f := range found {
   929  		if !f {
   930  			t.Fatal("missing post")
   931  		}
   932  	}
   933  
   934  	_, resp = Client.GetPostsForChannel("", 0, 60, "")
   935  	CheckBadRequestStatus(t, resp)
   936  
   937  	_, resp = Client.GetPostsForChannel("junk", 0, 60, "")
   938  	CheckBadRequestStatus(t, resp)
   939  
   940  	_, resp = Client.GetPostsForChannel(model.NewId(), 0, 60, "")
   941  	CheckForbiddenStatus(t, resp)
   942  
   943  	Client.Logout()
   944  	_, resp = Client.GetPostsForChannel(model.NewId(), 0, 60, "")
   945  	CheckUnauthorizedStatus(t, resp)
   946  
   947  	_, resp = th.SystemAdminClient.GetPostsForChannel(th.BasicChannel.Id, 0, 60, "")
   948  	CheckNoError(t, resp)
   949  }
   950  
   951  func TestGetFlaggedPostsForUser(t *testing.T) {
   952  	th := Setup().InitBasic()
   953  	defer th.TearDown()
   954  	Client := th.Client
   955  	user := th.BasicUser
   956  	team1 := th.BasicTeam
   957  	channel1 := th.BasicChannel
   958  	post1 := th.CreatePost()
   959  	channel2 := th.CreatePublicChannel()
   960  	post2 := th.CreatePostWithClient(Client, channel2)
   961  
   962  	preference := model.Preference{
   963  		UserId:   user.Id,
   964  		Category: model.PREFERENCE_CATEGORY_FLAGGED_POST,
   965  		Name:     post1.Id,
   966  		Value:    "true",
   967  	}
   968  	_, resp := Client.UpdatePreferences(user.Id, &model.Preferences{preference})
   969  	CheckNoError(t, resp)
   970  	preference.Name = post2.Id
   971  	_, resp = Client.UpdatePreferences(user.Id, &model.Preferences{preference})
   972  	CheckNoError(t, resp)
   973  
   974  	opl := model.NewPostList()
   975  	opl.AddPost(post1)
   976  	opl.AddOrder(post1.Id)
   977  
   978  	rpl, resp := Client.GetFlaggedPostsForUserInChannel(user.Id, channel1.Id, 0, 10)
   979  	CheckNoError(t, resp)
   980  
   981  	if len(rpl.Posts) != 1 {
   982  		t.Fatal("should have returned 1 post")
   983  	}
   984  
   985  	if !reflect.DeepEqual(rpl.Posts, opl.Posts) {
   986  		t.Fatal("posts should have matched")
   987  	}
   988  
   989  	rpl, resp = Client.GetFlaggedPostsForUserInChannel(user.Id, channel1.Id, 0, 1)
   990  	CheckNoError(t, resp)
   991  
   992  	if len(rpl.Posts) != 1 {
   993  		t.Fatal("should have returned 1 post")
   994  	}
   995  
   996  	rpl, resp = Client.GetFlaggedPostsForUserInChannel(user.Id, channel1.Id, 1, 1)
   997  	CheckNoError(t, resp)
   998  
   999  	if len(rpl.Posts) != 0 {
  1000  		t.Fatal("should be empty")
  1001  	}
  1002  
  1003  	rpl, resp = Client.GetFlaggedPostsForUserInChannel(user.Id, GenerateTestId(), 0, 10)
  1004  	CheckNoError(t, resp)
  1005  
  1006  	if len(rpl.Posts) != 0 {
  1007  		t.Fatal("should be empty")
  1008  	}
  1009  
  1010  	rpl, resp = Client.GetFlaggedPostsForUserInChannel(user.Id, "junk", 0, 10)
  1011  	CheckBadRequestStatus(t, resp)
  1012  
  1013  	if rpl != nil {
  1014  		t.Fatal("should be nil")
  1015  	}
  1016  
  1017  	opl.AddPost(post2)
  1018  	opl.AddOrder(post2.Id)
  1019  
  1020  	rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 0, 10)
  1021  	CheckNoError(t, resp)
  1022  
  1023  	if len(rpl.Posts) != 2 {
  1024  		t.Fatal("should have returned 2 posts")
  1025  	}
  1026  
  1027  	if !reflect.DeepEqual(rpl.Posts, opl.Posts) {
  1028  		t.Fatal("posts should have matched")
  1029  	}
  1030  
  1031  	rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 0, 1)
  1032  	CheckNoError(t, resp)
  1033  
  1034  	if len(rpl.Posts) != 1 {
  1035  		t.Fatal("should have returned 1 post")
  1036  	}
  1037  
  1038  	rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 1, 1)
  1039  	CheckNoError(t, resp)
  1040  
  1041  	if len(rpl.Posts) != 1 {
  1042  		t.Fatal("should have returned 1 post")
  1043  	}
  1044  
  1045  	rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 1000, 10)
  1046  	CheckNoError(t, resp)
  1047  
  1048  	if len(rpl.Posts) != 0 {
  1049  		t.Fatal("should be empty")
  1050  	}
  1051  
  1052  	rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, GenerateTestId(), 0, 10)
  1053  	CheckNoError(t, resp)
  1054  
  1055  	if len(rpl.Posts) != 0 {
  1056  		t.Fatal("should be empty")
  1057  	}
  1058  
  1059  	rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, "junk", 0, 10)
  1060  	CheckBadRequestStatus(t, resp)
  1061  
  1062  	if rpl != nil {
  1063  		t.Fatal("should be nil")
  1064  	}
  1065  
  1066  	channel3 := th.CreatePrivateChannel()
  1067  	post4 := th.CreatePostWithClient(Client, channel3)
  1068  
  1069  	preference.Name = post4.Id
  1070  	Client.UpdatePreferences(user.Id, &model.Preferences{preference})
  1071  
  1072  	opl.AddPost(post4)
  1073  	opl.AddOrder(post4.Id)
  1074  
  1075  	rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 0, 10)
  1076  	CheckNoError(t, resp)
  1077  
  1078  	if len(rpl.Posts) != 3 {
  1079  		t.Fatal("should have returned 3 posts")
  1080  	}
  1081  
  1082  	if !reflect.DeepEqual(rpl.Posts, opl.Posts) {
  1083  		t.Fatal("posts should have matched")
  1084  	}
  1085  
  1086  	rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 0, 2)
  1087  	CheckNoError(t, resp)
  1088  
  1089  	if len(rpl.Posts) != 2 {
  1090  		t.Fatal("should have returned 2 posts")
  1091  	}
  1092  
  1093  	rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 2, 2)
  1094  	CheckNoError(t, resp)
  1095  
  1096  	if len(rpl.Posts) != 1 {
  1097  		t.Fatal("should have returned 1 post")
  1098  	}
  1099  
  1100  	rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 1000, 10)
  1101  	CheckNoError(t, resp)
  1102  
  1103  	if len(rpl.Posts) != 0 {
  1104  		t.Fatal("should be empty")
  1105  	}
  1106  
  1107  	channel4 := th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE)
  1108  	post5 := th.CreatePostWithClient(th.SystemAdminClient, channel4)
  1109  
  1110  	preference.Name = post5.Id
  1111  	_, resp = Client.UpdatePreferences(user.Id, &model.Preferences{preference})
  1112  	CheckForbiddenStatus(t, resp)
  1113  
  1114  	rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 0, 10)
  1115  	CheckNoError(t, resp)
  1116  
  1117  	if len(rpl.Posts) != 3 {
  1118  		t.Fatal("should have returned 3 posts")
  1119  	}
  1120  
  1121  	if !reflect.DeepEqual(rpl.Posts, opl.Posts) {
  1122  		t.Fatal("posts should have matched")
  1123  	}
  1124  
  1125  	th.AddUserToChannel(user, channel4)
  1126  	_, resp = Client.UpdatePreferences(user.Id, &model.Preferences{preference})
  1127  	CheckNoError(t, resp)
  1128  
  1129  	rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 0, 10)
  1130  	CheckNoError(t, resp)
  1131  
  1132  	opl.AddPost(post5)
  1133  	opl.AddOrder(post5.Id)
  1134  
  1135  	if len(rpl.Posts) != 4 {
  1136  		t.Fatal("should have returned 4 posts")
  1137  	}
  1138  
  1139  	if !reflect.DeepEqual(rpl.Posts, opl.Posts) {
  1140  		t.Fatal("posts should have matched")
  1141  	}
  1142  
  1143  	err := th.App.RemoveUserFromChannel(user.Id, "", channel4)
  1144  	if err != nil {
  1145  		t.Error("Unable to remove user from channel")
  1146  	}
  1147  
  1148  	rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 0, 10)
  1149  	CheckNoError(t, resp)
  1150  
  1151  	opl2 := model.NewPostList()
  1152  	opl2.AddPost(post1)
  1153  	opl2.AddOrder(post1.Id)
  1154  	opl2.AddPost(post2)
  1155  	opl2.AddOrder(post2.Id)
  1156  	opl2.AddPost(post4)
  1157  	opl2.AddOrder(post4.Id)
  1158  
  1159  	if len(rpl.Posts) != 3 {
  1160  		t.Fatal("should have returned 3 posts")
  1161  	}
  1162  
  1163  	if !reflect.DeepEqual(rpl.Posts, opl2.Posts) {
  1164  		t.Fatal("posts should have matched")
  1165  	}
  1166  
  1167  	_, resp = Client.GetFlaggedPostsForUser("junk", 0, 10)
  1168  	CheckBadRequestStatus(t, resp)
  1169  
  1170  	_, resp = Client.GetFlaggedPostsForUser(GenerateTestId(), 0, 10)
  1171  	CheckForbiddenStatus(t, resp)
  1172  
  1173  	Client.Logout()
  1174  
  1175  	_, resp = Client.GetFlaggedPostsForUserInChannel(user.Id, channel1.Id, 0, 10)
  1176  	CheckUnauthorizedStatus(t, resp)
  1177  
  1178  	_, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 0, 10)
  1179  	CheckUnauthorizedStatus(t, resp)
  1180  
  1181  	_, resp = Client.GetFlaggedPostsForUser(user.Id, 0, 10)
  1182  	CheckUnauthorizedStatus(t, resp)
  1183  
  1184  	_, resp = th.SystemAdminClient.GetFlaggedPostsForUserInChannel(user.Id, channel1.Id, 0, 10)
  1185  	CheckNoError(t, resp)
  1186  
  1187  	_, resp = th.SystemAdminClient.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 0, 10)
  1188  	CheckNoError(t, resp)
  1189  
  1190  	_, resp = th.SystemAdminClient.GetFlaggedPostsForUser(user.Id, 0, 10)
  1191  	CheckNoError(t, resp)
  1192  }
  1193  
  1194  func TestGetPostsAfterAndBefore(t *testing.T) {
  1195  	th := Setup().InitBasic()
  1196  	defer th.TearDown()
  1197  	Client := th.Client
  1198  
  1199  	post1 := th.CreatePost()
  1200  	post2 := th.CreatePost()
  1201  	post3 := th.CreatePost()
  1202  	post4 := th.CreatePost()
  1203  	post5 := th.CreatePost()
  1204  
  1205  	posts, resp := Client.GetPostsBefore(th.BasicChannel.Id, post3.Id, 0, 100, "")
  1206  	CheckNoError(t, resp)
  1207  
  1208  	found := make([]bool, 2)
  1209  	for _, p := range posts.Posts {
  1210  		if p.Id == post1.Id {
  1211  			found[0] = true
  1212  		} else if p.Id == post2.Id {
  1213  			found[1] = true
  1214  		}
  1215  
  1216  		if p.Id == post4.Id || p.Id == post5.Id {
  1217  			t.Fatal("returned posts after")
  1218  		}
  1219  	}
  1220  
  1221  	for _, f := range found {
  1222  		if !f {
  1223  			t.Fatal("missing post")
  1224  		}
  1225  	}
  1226  
  1227  	posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post3.Id, 1, 1, "")
  1228  	CheckNoError(t, resp)
  1229  
  1230  	if len(posts.Posts) != 1 {
  1231  		t.Fatal("too many posts returned")
  1232  	}
  1233  
  1234  	posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, "junk", 1, 1, "")
  1235  	CheckNoError(t, resp)
  1236  
  1237  	if len(posts.Posts) != 0 {
  1238  		t.Fatal("should have no posts")
  1239  	}
  1240  
  1241  	posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post3.Id, 0, 100, "")
  1242  	CheckNoError(t, resp)
  1243  
  1244  	found = make([]bool, 2)
  1245  	for _, p := range posts.Posts {
  1246  		if p.Id == post4.Id {
  1247  			found[0] = true
  1248  		} else if p.Id == post5.Id {
  1249  			found[1] = true
  1250  		}
  1251  
  1252  		if p.Id == post1.Id || p.Id == post2.Id {
  1253  			t.Fatal("returned posts before")
  1254  		}
  1255  	}
  1256  
  1257  	for _, f := range found {
  1258  		if !f {
  1259  			t.Fatal("missing post")
  1260  		}
  1261  	}
  1262  
  1263  	posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post3.Id, 1, 1, "")
  1264  	CheckNoError(t, resp)
  1265  
  1266  	if len(posts.Posts) != 1 {
  1267  		t.Fatal("too many posts returned")
  1268  	}
  1269  
  1270  	posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, "junk", 1, 1, "")
  1271  	CheckNoError(t, resp)
  1272  
  1273  	if len(posts.Posts) != 0 {
  1274  		t.Fatal("should have no posts")
  1275  	}
  1276  }
  1277  
  1278  func TestGetPost(t *testing.T) {
  1279  	th := Setup().InitBasic()
  1280  	defer th.TearDown()
  1281  	Client := th.Client
  1282  
  1283  	post, resp := Client.GetPost(th.BasicPost.Id, "")
  1284  	CheckNoError(t, resp)
  1285  
  1286  	if post.Id != th.BasicPost.Id {
  1287  		t.Fatal("post ids don't match")
  1288  	}
  1289  
  1290  	post, resp = Client.GetPost(th.BasicPost.Id, resp.Etag)
  1291  	CheckEtag(t, post, resp)
  1292  
  1293  	_, resp = Client.GetPost("", "")
  1294  	CheckNotFoundStatus(t, resp)
  1295  
  1296  	_, resp = Client.GetPost("junk", "")
  1297  	CheckBadRequestStatus(t, resp)
  1298  
  1299  	_, resp = Client.GetPost(model.NewId(), "")
  1300  	CheckNotFoundStatus(t, resp)
  1301  
  1302  	Client.RemoveUserFromChannel(th.BasicChannel.Id, th.BasicUser.Id)
  1303  
  1304  	// Channel is public, should be able to read post
  1305  	_, resp = Client.GetPost(th.BasicPost.Id, "")
  1306  	CheckNoError(t, resp)
  1307  
  1308  	privatePost := th.CreatePostWithClient(Client, th.BasicPrivateChannel)
  1309  
  1310  	_, resp = Client.GetPost(privatePost.Id, "")
  1311  	CheckNoError(t, resp)
  1312  
  1313  	Client.RemoveUserFromChannel(th.BasicPrivateChannel.Id, th.BasicUser.Id)
  1314  
  1315  	// Channel is private, should not be able to read post
  1316  	_, resp = Client.GetPost(privatePost.Id, "")
  1317  	CheckForbiddenStatus(t, resp)
  1318  
  1319  	Client.Logout()
  1320  	_, resp = Client.GetPost(model.NewId(), "")
  1321  	CheckUnauthorizedStatus(t, resp)
  1322  
  1323  	_, resp = th.SystemAdminClient.GetPost(th.BasicPost.Id, "")
  1324  	CheckNoError(t, resp)
  1325  }
  1326  
  1327  func TestDeletePost(t *testing.T) {
  1328  	th := Setup().InitBasic()
  1329  	defer th.TearDown()
  1330  	Client := th.Client
  1331  
  1332  	_, resp := Client.DeletePost("")
  1333  	CheckNotFoundStatus(t, resp)
  1334  
  1335  	_, resp = Client.DeletePost("junk")
  1336  	CheckBadRequestStatus(t, resp)
  1337  
  1338  	_, resp = Client.DeletePost(th.BasicPost.Id)
  1339  	CheckForbiddenStatus(t, resp)
  1340  
  1341  	Client.Login(th.TeamAdminUser.Email, th.TeamAdminUser.Password)
  1342  	_, resp = Client.DeletePost(th.BasicPost.Id)
  1343  	CheckNoError(t, resp)
  1344  
  1345  	post := th.CreatePost()
  1346  	user := th.CreateUser()
  1347  
  1348  	Client.Logout()
  1349  	Client.Login(user.Email, user.Password)
  1350  
  1351  	_, resp = Client.DeletePost(post.Id)
  1352  	CheckForbiddenStatus(t, resp)
  1353  
  1354  	Client.Logout()
  1355  	_, resp = Client.DeletePost(model.NewId())
  1356  	CheckUnauthorizedStatus(t, resp)
  1357  
  1358  	status, resp := th.SystemAdminClient.DeletePost(post.Id)
  1359  	if !status {
  1360  		t.Fatal("post should return status OK")
  1361  	}
  1362  	CheckNoError(t, resp)
  1363  }
  1364  
  1365  func TestGetPostThread(t *testing.T) {
  1366  	th := Setup().InitBasic()
  1367  	defer th.TearDown()
  1368  	Client := th.Client
  1369  
  1370  	post := &model.Post{ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a", RootId: th.BasicPost.Id}
  1371  	post, _ = Client.CreatePost(post)
  1372  
  1373  	list, resp := Client.GetPostThread(th.BasicPost.Id, "")
  1374  	CheckNoError(t, resp)
  1375  
  1376  	var list2 *model.PostList
  1377  	list2, resp = Client.GetPostThread(th.BasicPost.Id, resp.Etag)
  1378  	CheckEtag(t, list2, resp)
  1379  
  1380  	if list.Order[0] != th.BasicPost.Id {
  1381  		t.Fatal("wrong order")
  1382  	}
  1383  
  1384  	if _, ok := list.Posts[th.BasicPost.Id]; !ok {
  1385  		t.Fatal("should have had post")
  1386  	}
  1387  
  1388  	if _, ok := list.Posts[post.Id]; !ok {
  1389  		t.Fatal("should have had post")
  1390  	}
  1391  
  1392  	_, resp = Client.GetPostThread("junk", "")
  1393  	CheckBadRequestStatus(t, resp)
  1394  
  1395  	_, resp = Client.GetPostThread(model.NewId(), "")
  1396  	CheckNotFoundStatus(t, resp)
  1397  
  1398  	Client.RemoveUserFromChannel(th.BasicChannel.Id, th.BasicUser.Id)
  1399  
  1400  	// Channel is public, should be able to read post
  1401  	_, resp = Client.GetPostThread(th.BasicPost.Id, "")
  1402  	CheckNoError(t, resp)
  1403  
  1404  	privatePost := th.CreatePostWithClient(Client, th.BasicPrivateChannel)
  1405  
  1406  	_, resp = Client.GetPostThread(privatePost.Id, "")
  1407  	CheckNoError(t, resp)
  1408  
  1409  	Client.RemoveUserFromChannel(th.BasicPrivateChannel.Id, th.BasicUser.Id)
  1410  
  1411  	// Channel is private, should not be able to read post
  1412  	_, resp = Client.GetPostThread(privatePost.Id, "")
  1413  	CheckForbiddenStatus(t, resp)
  1414  
  1415  	Client.Logout()
  1416  	_, resp = Client.GetPostThread(model.NewId(), "")
  1417  	CheckUnauthorizedStatus(t, resp)
  1418  
  1419  	_, resp = th.SystemAdminClient.GetPostThread(th.BasicPost.Id, "")
  1420  	CheckNoError(t, resp)
  1421  }
  1422  
  1423  func TestSearchPosts(t *testing.T) {
  1424  	th := Setup().InitBasic()
  1425  	defer th.TearDown()
  1426  	experimentalViewArchivedChannels := *th.App.Config().TeamSettings.ExperimentalViewArchivedChannels
  1427  	defer func() {
  1428  		th.App.UpdateConfig(func(cfg *model.Config) {
  1429  			cfg.TeamSettings.ExperimentalViewArchivedChannels = &experimentalViewArchivedChannels
  1430  		})
  1431  	}()
  1432  	th.App.UpdateConfig(func(cfg *model.Config) {
  1433  		*cfg.TeamSettings.ExperimentalViewArchivedChannels = true
  1434  	})
  1435  
  1436  	th.LoginBasic()
  1437  	Client := th.Client
  1438  
  1439  	message := "search for post1"
  1440  	_ = th.CreateMessagePost(message)
  1441  
  1442  	message = "search for post2"
  1443  	post2 := th.CreateMessagePost(message)
  1444  
  1445  	message = "#hashtag search for post3"
  1446  	post3 := th.CreateMessagePost(message)
  1447  
  1448  	message = "hashtag for post4"
  1449  	_ = th.CreateMessagePost(message)
  1450  
  1451  	archivedChannel := th.CreatePublicChannel()
  1452  	_ = th.CreateMessagePostWithClient(th.Client, archivedChannel, "#hashtag for post3")
  1453  	th.Client.DeleteChannel(archivedChannel.Id)
  1454  
  1455  	terms := "search"
  1456  	isOrSearch := false
  1457  	timezoneOffset := 5
  1458  	searchParams := model.SearchParameter{
  1459  		Terms:          &terms,
  1460  		IsOrSearch:     &isOrSearch,
  1461  		TimeZoneOffset: &timezoneOffset,
  1462  	}
  1463  	posts, resp := Client.SearchPostsWithParams(th.BasicTeam.Id, &searchParams)
  1464  	CheckNoError(t, resp)
  1465  	if len(posts.Order) != 3 {
  1466  		t.Fatal("wrong search")
  1467  	}
  1468  
  1469  	terms = "search"
  1470  	page := 0
  1471  	perPage := 2
  1472  	searchParams = model.SearchParameter{
  1473  		Terms:          &terms,
  1474  		IsOrSearch:     &isOrSearch,
  1475  		TimeZoneOffset: &timezoneOffset,
  1476  		Page:           &page,
  1477  		PerPage:        &perPage,
  1478  	}
  1479  	posts2, resp := Client.SearchPostsWithParams(th.BasicTeam.Id, &searchParams)
  1480  	CheckNoError(t, resp)
  1481  	if len(posts2.Order) != 3 { // We don't support paging for DB search yet, modify this when we do.
  1482  		t.Fatal("Wrong number of posts", len(posts2.Order))
  1483  	}
  1484  	assert.Equal(t, posts.Order[0], posts2.Order[0])
  1485  	assert.Equal(t, posts.Order[1], posts2.Order[1])
  1486  
  1487  	page = 1
  1488  	searchParams = model.SearchParameter{
  1489  		Terms:          &terms,
  1490  		IsOrSearch:     &isOrSearch,
  1491  		TimeZoneOffset: &timezoneOffset,
  1492  		Page:           &page,
  1493  		PerPage:        &perPage,
  1494  	}
  1495  	posts2, resp = Client.SearchPostsWithParams(th.BasicTeam.Id, &searchParams)
  1496  	CheckNoError(t, resp)
  1497  	if len(posts2.Order) != 0 { // We don't support paging for DB search yet, modify this when we do.
  1498  		t.Fatal("Wrong number of posts", len(posts2.Order))
  1499  	}
  1500  
  1501  	posts, resp = Client.SearchPosts(th.BasicTeam.Id, "search", false)
  1502  	CheckNoError(t, resp)
  1503  	if len(posts.Order) != 3 {
  1504  		t.Fatal("wrong search")
  1505  	}
  1506  
  1507  	posts, resp = Client.SearchPosts(th.BasicTeam.Id, "post2", false)
  1508  	CheckNoError(t, resp)
  1509  	if len(posts.Order) != 1 && posts.Order[0] == post2.Id {
  1510  		t.Fatal("wrong search")
  1511  	}
  1512  
  1513  	posts, resp = Client.SearchPosts(th.BasicTeam.Id, "#hashtag", false)
  1514  	CheckNoError(t, resp)
  1515  	if len(posts.Order) != 1 && posts.Order[0] == post3.Id {
  1516  		t.Fatal("wrong search")
  1517  	}
  1518  
  1519  	terms = "#hashtag"
  1520  	includeDeletedChannels := true
  1521  	searchParams = model.SearchParameter{
  1522  		Terms:                  &terms,
  1523  		IsOrSearch:             &isOrSearch,
  1524  		TimeZoneOffset:         &timezoneOffset,
  1525  		IncludeDeletedChannels: &includeDeletedChannels,
  1526  	}
  1527  	posts, resp = Client.SearchPostsWithParams(th.BasicTeam.Id, &searchParams)
  1528  	CheckNoError(t, resp)
  1529  	if len(posts.Order) != 2 {
  1530  		t.Fatal("wrong search")
  1531  	}
  1532  
  1533  	th.App.UpdateConfig(func(cfg *model.Config) {
  1534  		*cfg.TeamSettings.ExperimentalViewArchivedChannels = false
  1535  	})
  1536  
  1537  	posts, resp = Client.SearchPostsWithParams(th.BasicTeam.Id, &searchParams)
  1538  	CheckNoError(t, resp)
  1539  	if len(posts.Order) != 1 {
  1540  		t.Fatal("wrong search")
  1541  	}
  1542  
  1543  	if posts, _ = Client.SearchPosts(th.BasicTeam.Id, "*", false); len(posts.Order) != 0 {
  1544  		t.Fatal("searching for just * shouldn't return any results")
  1545  	}
  1546  
  1547  	posts, resp = Client.SearchPosts(th.BasicTeam.Id, "post1 post2", true)
  1548  	CheckNoError(t, resp)
  1549  
  1550  	if len(posts.Order) != 2 {
  1551  		t.Fatal("wrong search results")
  1552  	}
  1553  
  1554  	_, resp = Client.SearchPosts("junk", "#sgtitlereview", false)
  1555  	CheckBadRequestStatus(t, resp)
  1556  
  1557  	_, resp = Client.SearchPosts(model.NewId(), "#sgtitlereview", false)
  1558  	CheckForbiddenStatus(t, resp)
  1559  
  1560  	_, resp = Client.SearchPosts(th.BasicTeam.Id, "", false)
  1561  	CheckBadRequestStatus(t, resp)
  1562  
  1563  	Client.Logout()
  1564  	_, resp = Client.SearchPosts(th.BasicTeam.Id, "#sgtitlereview", false)
  1565  	CheckUnauthorizedStatus(t, resp)
  1566  }
  1567  
  1568  func TestSearchHashtagPosts(t *testing.T) {
  1569  	th := Setup().InitBasic()
  1570  	defer th.TearDown()
  1571  	th.LoginBasic()
  1572  	Client := th.Client
  1573  
  1574  	message := "#sgtitlereview with space"
  1575  	assert.NotNil(t, th.CreateMessagePost(message))
  1576  
  1577  	message = "#sgtitlereview\n with return"
  1578  	assert.NotNil(t, th.CreateMessagePost(message))
  1579  
  1580  	message = "no hashtag"
  1581  	assert.NotNil(t, th.CreateMessagePost(message))
  1582  
  1583  	posts, resp := Client.SearchPosts(th.BasicTeam.Id, "#sgtitlereview", false)
  1584  	CheckNoError(t, resp)
  1585  	if len(posts.Order) != 2 {
  1586  		t.Fatal("wrong search results")
  1587  	}
  1588  
  1589  	Client.Logout()
  1590  	_, resp = Client.SearchPosts(th.BasicTeam.Id, "#sgtitlereview", false)
  1591  	CheckUnauthorizedStatus(t, resp)
  1592  }
  1593  
  1594  func TestSearchPostsInChannel(t *testing.T) {
  1595  	th := Setup().InitBasic()
  1596  	defer th.TearDown()
  1597  	th.LoginBasic()
  1598  	Client := th.Client
  1599  
  1600  	channel := th.CreatePublicChannel()
  1601  
  1602  	message := "sgtitlereview with space"
  1603  	_ = th.CreateMessagePost(message)
  1604  
  1605  	message = "sgtitlereview\n with return"
  1606  	_ = th.CreateMessagePostWithClient(Client, th.BasicChannel2, message)
  1607  
  1608  	message = "other message with no return"
  1609  	_ = th.CreateMessagePostWithClient(Client, th.BasicChannel2, message)
  1610  
  1611  	message = "other message with no return"
  1612  	_ = th.CreateMessagePostWithClient(Client, channel, message)
  1613  
  1614  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "channel:", false); len(posts.Order) != 0 {
  1615  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1616  	}
  1617  
  1618  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "in:", false); len(posts.Order) != 0 {
  1619  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1620  	}
  1621  
  1622  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "channel:"+th.BasicChannel.Name, false); len(posts.Order) != 2 {
  1623  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1624  	}
  1625  
  1626  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "in:"+th.BasicChannel2.Name, false); len(posts.Order) != 2 {
  1627  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1628  	}
  1629  
  1630  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "channel:"+th.BasicChannel2.Name, false); len(posts.Order) != 2 {
  1631  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1632  	}
  1633  
  1634  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "ChAnNeL:"+th.BasicChannel2.Name, false); len(posts.Order) != 2 {
  1635  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1636  	}
  1637  
  1638  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "sgtitlereview", false); len(posts.Order) != 2 {
  1639  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1640  	}
  1641  
  1642  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "sgtitlereview channel:"+th.BasicChannel.Name, false); len(posts.Order) != 1 {
  1643  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1644  	}
  1645  
  1646  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "sgtitlereview in: "+th.BasicChannel2.Name, false); len(posts.Order) != 1 {
  1647  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1648  	}
  1649  
  1650  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "sgtitlereview channel: "+th.BasicChannel2.Name, false); len(posts.Order) != 1 {
  1651  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1652  	}
  1653  
  1654  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "channel: "+th.BasicChannel2.Name+" channel: "+channel.Name, false); len(posts.Order) != 3 {
  1655  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1656  	}
  1657  
  1658  }
  1659  
  1660  func TestSearchPostsFromUser(t *testing.T) {
  1661  	th := Setup().InitBasic()
  1662  	defer th.TearDown()
  1663  	Client := th.Client
  1664  
  1665  	th.LoginTeamAdmin()
  1666  	user := th.CreateUser()
  1667  	th.LinkUserToTeam(user, th.BasicTeam)
  1668  	th.App.AddUserToChannel(user, th.BasicChannel)
  1669  	th.App.AddUserToChannel(user, th.BasicChannel2)
  1670  
  1671  	message := "sgtitlereview with space"
  1672  	_ = th.CreateMessagePost(message)
  1673  
  1674  	Client.Logout()
  1675  	th.LoginBasic2()
  1676  
  1677  	message = "sgtitlereview\n with return"
  1678  	_ = th.CreateMessagePostWithClient(Client, th.BasicChannel2, message)
  1679  
  1680  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.TeamAdminUser.Username, false); len(posts.Order) != 2 {
  1681  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1682  	}
  1683  
  1684  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username, false); len(posts.Order) != 1 {
  1685  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1686  	}
  1687  
  1688  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username+" sgtitlereview", false); len(posts.Order) != 1 {
  1689  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1690  	}
  1691  
  1692  	message = "hullo"
  1693  	_ = th.CreateMessagePost(message)
  1694  
  1695  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username+" in:"+th.BasicChannel.Name, false); len(posts.Order) != 1 {
  1696  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1697  	}
  1698  
  1699  	Client.Login(user.Email, user.Password)
  1700  
  1701  	// wait for the join/leave messages to be created for user3 since they're done asynchronously
  1702  	time.Sleep(100 * time.Millisecond)
  1703  
  1704  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username, false); len(posts.Order) != 2 {
  1705  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1706  	}
  1707  
  1708  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username+" from: "+user.Username, false); len(posts.Order) != 2 {
  1709  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1710  	}
  1711  
  1712  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username+" from: "+user.Username+" in:"+th.BasicChannel2.Name, false); len(posts.Order) != 1 {
  1713  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1714  	}
  1715  
  1716  	message = "coconut"
  1717  	_ = th.CreateMessagePostWithClient(Client, th.BasicChannel2, message)
  1718  
  1719  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username+" from: "+user.Username+" in:"+th.BasicChannel2.Name+" coconut", false); len(posts.Order) != 1 {
  1720  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1721  	}
  1722  }
  1723  
  1724  func TestSearchPostsWithDateFlags(t *testing.T) {
  1725  	th := Setup().InitBasic()
  1726  	defer th.TearDown()
  1727  	th.LoginBasic()
  1728  	Client := th.Client
  1729  
  1730  	message := "sgtitlereview\n with return"
  1731  	createDate := time.Date(2018, 8, 1, 5, 0, 0, 0, time.UTC)
  1732  	_ = th.CreateMessagePostNoClient(th.BasicChannel, message, utils.MillisFromTime(createDate))
  1733  
  1734  	message = "other message with no return"
  1735  	createDate = time.Date(2018, 8, 2, 5, 0, 0, 0, time.UTC)
  1736  	_ = th.CreateMessagePostNoClient(th.BasicChannel, message, utils.MillisFromTime(createDate))
  1737  
  1738  	message = "other message with no return"
  1739  	createDate = time.Date(2018, 8, 3, 5, 0, 0, 0, time.UTC)
  1740  	_ = th.CreateMessagePostNoClient(th.BasicChannel, message, utils.MillisFromTime(createDate))
  1741  
  1742  	posts, _ := Client.SearchPosts(th.BasicTeam.Id, "return", false)
  1743  	if len(posts.Order) != 3 {
  1744  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1745  	}
  1746  
  1747  	posts, _ = Client.SearchPosts(th.BasicTeam.Id, "on:", false)
  1748  	if len(posts.Order) != 0 {
  1749  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1750  	}
  1751  
  1752  	posts, _ = Client.SearchPosts(th.BasicTeam.Id, "after:", false)
  1753  	if len(posts.Order) != 0 {
  1754  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1755  	}
  1756  
  1757  	posts, _ = Client.SearchPosts(th.BasicTeam.Id, "before:", false)
  1758  	if len(posts.Order) != 0 {
  1759  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1760  	}
  1761  
  1762  	posts, _ = Client.SearchPosts(th.BasicTeam.Id, "on:2018-08-01", false)
  1763  	if len(posts.Order) != 1 {
  1764  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1765  	}
  1766  
  1767  	posts, _ = Client.SearchPosts(th.BasicTeam.Id, "after:2018-08-01", false)
  1768  	resultCount := 0
  1769  	for _, post := range posts.Posts {
  1770  		if post.UserId == th.BasicUser.Id {
  1771  			resultCount = resultCount + 1
  1772  		}
  1773  	}
  1774  	if resultCount != 2 {
  1775  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1776  	}
  1777  
  1778  	posts, _ = Client.SearchPosts(th.BasicTeam.Id, "before:2018-08-02", false)
  1779  	if len(posts.Order) != 1 {
  1780  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1781  	}
  1782  
  1783  	posts, _ = Client.SearchPosts(th.BasicTeam.Id, "before:2018-08-03 after:2018-08-02", false)
  1784  	if len(posts.Order) != 0 {
  1785  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1786  	}
  1787  
  1788  	posts, _ = Client.SearchPosts(th.BasicTeam.Id, "before:2018-08-03 after:2018-08-01", false)
  1789  	if len(posts.Order) != 1 {
  1790  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1791  	}
  1792  }
  1793  
  1794  func TestGetFileInfosForPost(t *testing.T) {
  1795  	th := Setup().InitBasic()
  1796  	defer th.TearDown()
  1797  	Client := th.Client
  1798  
  1799  	fileIds := make([]string, 3)
  1800  	if data, err := testutils.ReadTestFile("test.png"); err != nil {
  1801  		t.Fatal(err)
  1802  	} else {
  1803  		for i := 0; i < 3; i++ {
  1804  			fileResp, _ := Client.UploadFile(data, th.BasicChannel.Id, "test.png")
  1805  			fileIds[i] = fileResp.FileInfos[0].Id
  1806  		}
  1807  	}
  1808  
  1809  	post := &model.Post{ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a", FileIds: fileIds}
  1810  	post, _ = Client.CreatePost(post)
  1811  
  1812  	infos, resp := Client.GetFileInfosForPost(post.Id, "")
  1813  	CheckNoError(t, resp)
  1814  
  1815  	if len(infos) != 3 {
  1816  		t.Fatal("missing file infos")
  1817  	}
  1818  
  1819  	found := false
  1820  	for _, info := range infos {
  1821  		if info.Id == fileIds[0] {
  1822  			found = true
  1823  		}
  1824  	}
  1825  
  1826  	if !found {
  1827  		t.Fatal("missing file info")
  1828  	}
  1829  
  1830  	infos, resp = Client.GetFileInfosForPost(post.Id, resp.Etag)
  1831  	CheckEtag(t, infos, resp)
  1832  
  1833  	infos, resp = Client.GetFileInfosForPost(th.BasicPost.Id, "")
  1834  	CheckNoError(t, resp)
  1835  
  1836  	if len(infos) != 0 {
  1837  		t.Fatal("should have no file infos")
  1838  	}
  1839  
  1840  	_, resp = Client.GetFileInfosForPost("junk", "")
  1841  	CheckBadRequestStatus(t, resp)
  1842  
  1843  	_, resp = Client.GetFileInfosForPost(model.NewId(), "")
  1844  	CheckForbiddenStatus(t, resp)
  1845  
  1846  	Client.Logout()
  1847  	_, resp = Client.GetFileInfosForPost(model.NewId(), "")
  1848  	CheckUnauthorizedStatus(t, resp)
  1849  
  1850  	_, resp = th.SystemAdminClient.GetFileInfosForPost(th.BasicPost.Id, "")
  1851  	CheckNoError(t, resp)
  1852  }