github.com/xzl8028/xenia-server@v0.0.0-20190809101854-18450a97da63/api4/post_test.go (about)

     1  // Copyright (c) 2017-present Xenia, 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/xzl8028/xenia-server/app"
    21  	"github.com/xzl8028/xenia-server/model"
    22  	"github.com/xzl8028/xenia-server/utils"
    23  	"github.com/xzl8028/xenia-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.xenia.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  	Client.Logout()
   606  	_, resp = Client.UpdatePost(rpost.Id, rpost)
   607  	CheckUnauthorizedStatus(t, resp)
   608  
   609  	th.LoginBasic2()
   610  	_, resp = Client.UpdatePost(rpost.Id, rpost)
   611  	CheckForbiddenStatus(t, resp)
   612  
   613  	Client.Logout()
   614  
   615  	th.LoginTeamAdmin()
   616  	_, resp = Client.UpdatePost(rpost.Id, rpost)
   617  	CheckForbiddenStatus(t, resp)
   618  
   619  	Client.Logout()
   620  
   621  	_, resp = th.SystemAdminClient.UpdatePost(rpost.Id, rpost)
   622  	CheckNoError(t, resp)
   623  }
   624  
   625  func TestUpdateOthersPostInDirectMessageChannel(t *testing.T) {
   626  	// This test checks that a sysadmin with the "EDIT_OTHERS_POSTS" permission can edit someone else's post in a
   627  	// channel without a team (DM/GM). This indirectly checks for the proper cascading all the way to system-wide roles
   628  	// on the user object of permissions based on a post in a channel with no team ID.
   629  	th := Setup().InitBasic()
   630  	defer th.TearDown()
   631  
   632  	dmChannel := th.CreateDmChannel(th.SystemAdminUser)
   633  
   634  	post := &model.Post{
   635  		Message:       "asd",
   636  		ChannelId:     dmChannel.Id,
   637  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   638  		UserId:        th.BasicUser.Id,
   639  		CreateAt:      0,
   640  	}
   641  
   642  	post, resp := th.Client.CreatePost(post)
   643  	CheckNoError(t, resp)
   644  
   645  	post.Message = "changed"
   646  	post, resp = th.SystemAdminClient.UpdatePost(post.Id, post)
   647  	CheckNoError(t, resp)
   648  }
   649  
   650  func TestPatchPost(t *testing.T) {
   651  	th := Setup().InitBasic()
   652  	defer th.TearDown()
   653  	Client := th.Client
   654  	channel := th.BasicChannel
   655  
   656  	th.App.SetLicense(model.NewTestLicense())
   657  
   658  	post := &model.Post{
   659  		ChannelId:    channel.Id,
   660  		IsPinned:     true,
   661  		Message:      "#hashtag a message",
   662  		Props:        model.StringInterface{"channel_header": "old_header"},
   663  		FileIds:      model.StringArray{"file1", "file2"},
   664  		HasReactions: true,
   665  	}
   666  	post, _ = Client.CreatePost(post)
   667  
   668  	patch := &model.PostPatch{}
   669  
   670  	patch.IsPinned = model.NewBool(false)
   671  	patch.Message = model.NewString("#otherhashtag other message")
   672  	patch.Props = new(model.StringInterface)
   673  	*patch.Props = model.StringInterface{"channel_header": "new_header"}
   674  	patch.FileIds = new(model.StringArray)
   675  	*patch.FileIds = model.StringArray{"file1", "otherfile2", "otherfile3"}
   676  	patch.HasReactions = model.NewBool(false)
   677  
   678  	rpost, resp := Client.PatchPost(post.Id, patch)
   679  	CheckNoError(t, resp)
   680  
   681  	if rpost.IsPinned {
   682  		t.Fatal("IsPinned did not update properly")
   683  	}
   684  	if rpost.Message != "#otherhashtag other message" {
   685  		t.Fatal("Message did not update properly")
   686  	}
   687  	if len(rpost.Props) != 1 {
   688  		t.Fatal("Props did not update properly")
   689  	}
   690  	if !reflect.DeepEqual(rpost.Props, *patch.Props) {
   691  		t.Fatal("Props did not update properly")
   692  	}
   693  	if rpost.Hashtags != "#otherhashtag" {
   694  		t.Fatal("Message did not update properly")
   695  	}
   696  	if len(rpost.FileIds) == 3 {
   697  		t.Fatal("FileIds should not update properly")
   698  	}
   699  	if reflect.DeepEqual(rpost.FileIds, *patch.FileIds) {
   700  		t.Fatal("FileIds should not update")
   701  	}
   702  	if rpost.HasReactions {
   703  		t.Fatal("HasReactions did not update properly")
   704  	}
   705  
   706  	patch2 := &model.PostPatch{}
   707  	attachments := []model.SlackAttachment{
   708  		{
   709  			Text: "Hello World",
   710  		},
   711  	}
   712  	patch2.Props = new(model.StringInterface)
   713  	*patch2.Props = model.StringInterface{"attachments": attachments}
   714  
   715  	rpost2, resp := Client.PatchPost(post.Id, patch2)
   716  	CheckNoError(t, resp)
   717  	assert.NotEmpty(t, rpost2.Props["attachments"])
   718  	assert.NotEqual(t, rpost.EditAt, rpost2.EditAt)
   719  
   720  	if r, err := Client.DoApiPut("/posts/"+post.Id+"/patch", "garbage"); err == nil {
   721  		t.Fatal("should have errored")
   722  	} else {
   723  		if r.StatusCode != http.StatusBadRequest {
   724  			t.Log("actual: " + strconv.Itoa(r.StatusCode))
   725  			t.Log("expected: " + strconv.Itoa(http.StatusBadRequest))
   726  			t.Fatal("wrong status code")
   727  		}
   728  	}
   729  
   730  	_, resp = Client.PatchPost("junk", patch)
   731  	CheckBadRequestStatus(t, resp)
   732  
   733  	_, resp = Client.PatchPost(GenerateTestId(), patch)
   734  	CheckForbiddenStatus(t, resp)
   735  
   736  	Client.Logout()
   737  	_, resp = Client.PatchPost(post.Id, patch)
   738  	CheckUnauthorizedStatus(t, resp)
   739  
   740  	th.LoginBasic2()
   741  	_, resp = Client.PatchPost(post.Id, patch)
   742  	CheckForbiddenStatus(t, resp)
   743  
   744  	th.LoginTeamAdmin()
   745  	_, resp = Client.PatchPost(post.Id, patch)
   746  	CheckForbiddenStatus(t, resp)
   747  
   748  	_, resp = th.SystemAdminClient.PatchPost(post.Id, patch)
   749  	CheckNoError(t, resp)
   750  }
   751  
   752  func TestPinPost(t *testing.T) {
   753  	th := Setup().InitBasic()
   754  	defer th.TearDown()
   755  	Client := th.Client
   756  
   757  	post := th.BasicPost
   758  	pass, resp := Client.PinPost(post.Id)
   759  	CheckNoError(t, resp)
   760  
   761  	if !pass {
   762  		t.Fatal("should have passed")
   763  	}
   764  
   765  	if rpost, err := th.App.GetSinglePost(post.Id); err != nil && !rpost.IsPinned {
   766  		t.Fatal("failed to pin post")
   767  	}
   768  
   769  	pass, resp = Client.PinPost("junk")
   770  	CheckBadRequestStatus(t, resp)
   771  
   772  	if pass {
   773  		t.Fatal("should have failed")
   774  	}
   775  
   776  	_, resp = Client.PinPost(GenerateTestId())
   777  	CheckForbiddenStatus(t, resp)
   778  
   779  	t.Run("unable-to-pin-post-in-read-only-town-square", func(t *testing.T) {
   780  		townSquareIsReadOnly := *th.App.Config().TeamSettings.ExperimentalTownSquareIsReadOnly
   781  		th.App.SetLicense(model.NewTestLicense())
   782  		th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.ExperimentalTownSquareIsReadOnly = true })
   783  
   784  		defer th.App.RemoveLicense()
   785  		defer th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.ExperimentalTownSquareIsReadOnly = townSquareIsReadOnly })
   786  
   787  		channel, err := th.App.GetChannelByName("town-square", th.BasicTeam.Id, true)
   788  		assert.Nil(t, err)
   789  		adminPost := th.CreatePostWithClient(th.SystemAdminClient, channel)
   790  
   791  		_, resp = Client.PinPost(adminPost.Id)
   792  		CheckForbiddenStatus(t, resp)
   793  	})
   794  
   795  	Client.Logout()
   796  	_, resp = Client.PinPost(post.Id)
   797  	CheckUnauthorizedStatus(t, resp)
   798  
   799  	_, resp = th.SystemAdminClient.PinPost(post.Id)
   800  	CheckNoError(t, resp)
   801  }
   802  
   803  func TestUnpinPost(t *testing.T) {
   804  	th := Setup().InitBasic()
   805  	defer th.TearDown()
   806  	Client := th.Client
   807  
   808  	pinnedPost := th.CreatePinnedPost()
   809  	pass, resp := Client.UnpinPost(pinnedPost.Id)
   810  	CheckNoError(t, resp)
   811  
   812  	if !pass {
   813  		t.Fatal("should have passed")
   814  	}
   815  
   816  	if rpost, err := th.App.GetSinglePost(pinnedPost.Id); err != nil && rpost.IsPinned {
   817  		t.Fatal("failed to pin post")
   818  	}
   819  
   820  	pass, resp = Client.UnpinPost("junk")
   821  	CheckBadRequestStatus(t, resp)
   822  
   823  	if pass {
   824  		t.Fatal("should have failed")
   825  	}
   826  
   827  	_, resp = Client.UnpinPost(GenerateTestId())
   828  	CheckForbiddenStatus(t, resp)
   829  
   830  	Client.Logout()
   831  	_, resp = Client.UnpinPost(pinnedPost.Id)
   832  	CheckUnauthorizedStatus(t, resp)
   833  
   834  	_, resp = th.SystemAdminClient.UnpinPost(pinnedPost.Id)
   835  	CheckNoError(t, resp)
   836  }
   837  
   838  func TestGetPostsForChannel(t *testing.T) {
   839  	th := Setup().InitBasic()
   840  	defer th.TearDown()
   841  	Client := th.Client
   842  
   843  	post1 := th.CreatePost()
   844  	post2 := th.CreatePost()
   845  	post3 := &model.Post{ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a", RootId: post1.Id}
   846  	post3, _ = Client.CreatePost(post3)
   847  
   848  	time.Sleep(300 * time.Millisecond)
   849  	since := model.GetMillis()
   850  	time.Sleep(300 * time.Millisecond)
   851  
   852  	post4 := th.CreatePost()
   853  
   854  	posts, resp := Client.GetPostsForChannel(th.BasicChannel.Id, 0, 60, "")
   855  	CheckNoError(t, resp)
   856  
   857  	if posts.Order[0] != post4.Id {
   858  		t.Fatal("wrong order")
   859  	}
   860  
   861  	if posts.Order[1] != post3.Id {
   862  		t.Fatal("wrong order")
   863  	}
   864  
   865  	if posts.Order[2] != post2.Id {
   866  		t.Fatal("wrong order")
   867  	}
   868  
   869  	if posts.Order[3] != post1.Id {
   870  		t.Fatal("wrong order")
   871  	}
   872  
   873  	posts, resp = Client.GetPostsForChannel(th.BasicChannel.Id, 0, 3, resp.Etag)
   874  	CheckEtag(t, posts, resp)
   875  
   876  	posts, resp = Client.GetPostsForChannel(th.BasicChannel.Id, 0, 3, "")
   877  	CheckNoError(t, resp)
   878  
   879  	if len(posts.Order) != 3 {
   880  		t.Fatal("wrong number returned")
   881  	}
   882  
   883  	if _, ok := posts.Posts[post3.Id]; !ok {
   884  		t.Fatal("missing comment")
   885  	}
   886  
   887  	if _, ok := posts.Posts[post1.Id]; !ok {
   888  		t.Fatal("missing root post")
   889  	}
   890  
   891  	posts, resp = Client.GetPostsForChannel(th.BasicChannel.Id, 1, 1, "")
   892  	CheckNoError(t, resp)
   893  
   894  	if posts.Order[0] != post3.Id {
   895  		t.Fatal("wrong order")
   896  	}
   897  
   898  	posts, resp = Client.GetPostsForChannel(th.BasicChannel.Id, 10000, 10000, "")
   899  	CheckNoError(t, resp)
   900  
   901  	if len(posts.Order) != 0 {
   902  		t.Fatal("should be no posts")
   903  	}
   904  
   905  	post5 := th.CreatePost()
   906  
   907  	posts, resp = Client.GetPostsSince(th.BasicChannel.Id, since)
   908  	CheckNoError(t, resp)
   909  
   910  	if len(posts.Posts) != 2 {
   911  		t.Log(posts.Posts)
   912  		t.Fatal("should return 2 posts")
   913  	}
   914  
   915  	found := make([]bool, 2)
   916  	for _, p := range posts.Posts {
   917  		if p.CreateAt < since {
   918  			t.Fatal("bad create at for post returned")
   919  		}
   920  		if p.Id == post4.Id {
   921  			found[0] = true
   922  		} else if p.Id == post5.Id {
   923  			found[1] = true
   924  		}
   925  	}
   926  
   927  	for _, f := range found {
   928  		if !f {
   929  			t.Fatal("missing post")
   930  		}
   931  	}
   932  
   933  	_, resp = Client.GetPostsForChannel("", 0, 60, "")
   934  	CheckBadRequestStatus(t, resp)
   935  
   936  	_, resp = Client.GetPostsForChannel("junk", 0, 60, "")
   937  	CheckBadRequestStatus(t, resp)
   938  
   939  	_, resp = Client.GetPostsForChannel(model.NewId(), 0, 60, "")
   940  	CheckForbiddenStatus(t, resp)
   941  
   942  	Client.Logout()
   943  	_, resp = Client.GetPostsForChannel(model.NewId(), 0, 60, "")
   944  	CheckUnauthorizedStatus(t, resp)
   945  
   946  	_, resp = th.SystemAdminClient.GetPostsForChannel(th.BasicChannel.Id, 0, 60, "")
   947  	CheckNoError(t, resp)
   948  }
   949  
   950  func TestGetFlaggedPostsForUser(t *testing.T) {
   951  	th := Setup().InitBasic()
   952  	defer th.TearDown()
   953  	Client := th.Client
   954  	user := th.BasicUser
   955  	team1 := th.BasicTeam
   956  	channel1 := th.BasicChannel
   957  	post1 := th.CreatePost()
   958  	channel2 := th.CreatePublicChannel()
   959  	post2 := th.CreatePostWithClient(Client, channel2)
   960  
   961  	preference := model.Preference{
   962  		UserId:   user.Id,
   963  		Category: model.PREFERENCE_CATEGORY_FLAGGED_POST,
   964  		Name:     post1.Id,
   965  		Value:    "true",
   966  	}
   967  	_, resp := Client.UpdatePreferences(user.Id, &model.Preferences{preference})
   968  	CheckNoError(t, resp)
   969  	preference.Name = post2.Id
   970  	_, resp = Client.UpdatePreferences(user.Id, &model.Preferences{preference})
   971  	CheckNoError(t, resp)
   972  
   973  	opl := model.NewPostList()
   974  	opl.AddPost(post1)
   975  	opl.AddOrder(post1.Id)
   976  
   977  	rpl, resp := Client.GetFlaggedPostsForUserInChannel(user.Id, channel1.Id, 0, 10)
   978  	CheckNoError(t, resp)
   979  
   980  	if len(rpl.Posts) != 1 {
   981  		t.Fatal("should have returned 1 post")
   982  	}
   983  
   984  	if !reflect.DeepEqual(rpl.Posts, opl.Posts) {
   985  		t.Fatal("posts should have matched")
   986  	}
   987  
   988  	rpl, resp = Client.GetFlaggedPostsForUserInChannel(user.Id, channel1.Id, 0, 1)
   989  	CheckNoError(t, resp)
   990  
   991  	if len(rpl.Posts) != 1 {
   992  		t.Fatal("should have returned 1 post")
   993  	}
   994  
   995  	rpl, resp = Client.GetFlaggedPostsForUserInChannel(user.Id, channel1.Id, 1, 1)
   996  	CheckNoError(t, resp)
   997  
   998  	if len(rpl.Posts) != 0 {
   999  		t.Fatal("should be empty")
  1000  	}
  1001  
  1002  	rpl, resp = Client.GetFlaggedPostsForUserInChannel(user.Id, GenerateTestId(), 0, 10)
  1003  	CheckNoError(t, resp)
  1004  
  1005  	if len(rpl.Posts) != 0 {
  1006  		t.Fatal("should be empty")
  1007  	}
  1008  
  1009  	rpl, resp = Client.GetFlaggedPostsForUserInChannel(user.Id, "junk", 0, 10)
  1010  	CheckBadRequestStatus(t, resp)
  1011  
  1012  	if rpl != nil {
  1013  		t.Fatal("should be nil")
  1014  	}
  1015  
  1016  	opl.AddPost(post2)
  1017  	opl.AddOrder(post2.Id)
  1018  
  1019  	rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 0, 10)
  1020  	CheckNoError(t, resp)
  1021  
  1022  	if len(rpl.Posts) != 2 {
  1023  		t.Fatal("should have returned 2 posts")
  1024  	}
  1025  
  1026  	if !reflect.DeepEqual(rpl.Posts, opl.Posts) {
  1027  		t.Fatal("posts should have matched")
  1028  	}
  1029  
  1030  	rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 0, 1)
  1031  	CheckNoError(t, resp)
  1032  
  1033  	if len(rpl.Posts) != 1 {
  1034  		t.Fatal("should have returned 1 post")
  1035  	}
  1036  
  1037  	rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 1, 1)
  1038  	CheckNoError(t, resp)
  1039  
  1040  	if len(rpl.Posts) != 1 {
  1041  		t.Fatal("should have returned 1 post")
  1042  	}
  1043  
  1044  	rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 1000, 10)
  1045  	CheckNoError(t, resp)
  1046  
  1047  	if len(rpl.Posts) != 0 {
  1048  		t.Fatal("should be empty")
  1049  	}
  1050  
  1051  	rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, GenerateTestId(), 0, 10)
  1052  	CheckNoError(t, resp)
  1053  
  1054  	if len(rpl.Posts) != 0 {
  1055  		t.Fatal("should be empty")
  1056  	}
  1057  
  1058  	rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, "junk", 0, 10)
  1059  	CheckBadRequestStatus(t, resp)
  1060  
  1061  	if rpl != nil {
  1062  		t.Fatal("should be nil")
  1063  	}
  1064  
  1065  	channel3 := th.CreatePrivateChannel()
  1066  	post4 := th.CreatePostWithClient(Client, channel3)
  1067  
  1068  	preference.Name = post4.Id
  1069  	Client.UpdatePreferences(user.Id, &model.Preferences{preference})
  1070  
  1071  	opl.AddPost(post4)
  1072  	opl.AddOrder(post4.Id)
  1073  
  1074  	rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 0, 10)
  1075  	CheckNoError(t, resp)
  1076  
  1077  	if len(rpl.Posts) != 3 {
  1078  		t.Fatal("should have returned 3 posts")
  1079  	}
  1080  
  1081  	if !reflect.DeepEqual(rpl.Posts, opl.Posts) {
  1082  		t.Fatal("posts should have matched")
  1083  	}
  1084  
  1085  	rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 0, 2)
  1086  	CheckNoError(t, resp)
  1087  
  1088  	if len(rpl.Posts) != 2 {
  1089  		t.Fatal("should have returned 2 posts")
  1090  	}
  1091  
  1092  	rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 2, 2)
  1093  	CheckNoError(t, resp)
  1094  
  1095  	if len(rpl.Posts) != 1 {
  1096  		t.Fatal("should have returned 1 post")
  1097  	}
  1098  
  1099  	rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 1000, 10)
  1100  	CheckNoError(t, resp)
  1101  
  1102  	if len(rpl.Posts) != 0 {
  1103  		t.Fatal("should be empty")
  1104  	}
  1105  
  1106  	channel4 := th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE)
  1107  	post5 := th.CreatePostWithClient(th.SystemAdminClient, channel4)
  1108  
  1109  	preference.Name = post5.Id
  1110  	_, resp = Client.UpdatePreferences(user.Id, &model.Preferences{preference})
  1111  	CheckForbiddenStatus(t, resp)
  1112  
  1113  	rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 0, 10)
  1114  	CheckNoError(t, resp)
  1115  
  1116  	if len(rpl.Posts) != 3 {
  1117  		t.Fatal("should have returned 3 posts")
  1118  	}
  1119  
  1120  	if !reflect.DeepEqual(rpl.Posts, opl.Posts) {
  1121  		t.Fatal("posts should have matched")
  1122  	}
  1123  
  1124  	th.AddUserToChannel(user, channel4)
  1125  	_, resp = Client.UpdatePreferences(user.Id, &model.Preferences{preference})
  1126  	CheckNoError(t, resp)
  1127  
  1128  	rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 0, 10)
  1129  	CheckNoError(t, resp)
  1130  
  1131  	opl.AddPost(post5)
  1132  	opl.AddOrder(post5.Id)
  1133  
  1134  	if len(rpl.Posts) != 4 {
  1135  		t.Fatal("should have returned 4 posts")
  1136  	}
  1137  
  1138  	if !reflect.DeepEqual(rpl.Posts, opl.Posts) {
  1139  		t.Fatal("posts should have matched")
  1140  	}
  1141  
  1142  	err := th.App.RemoveUserFromChannel(user.Id, "", channel4)
  1143  	if err != nil {
  1144  		t.Error("Unable to remove user from channel")
  1145  	}
  1146  
  1147  	rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 0, 10)
  1148  	CheckNoError(t, resp)
  1149  
  1150  	opl2 := model.NewPostList()
  1151  	opl2.AddPost(post1)
  1152  	opl2.AddOrder(post1.Id)
  1153  	opl2.AddPost(post2)
  1154  	opl2.AddOrder(post2.Id)
  1155  	opl2.AddPost(post4)
  1156  	opl2.AddOrder(post4.Id)
  1157  
  1158  	if len(rpl.Posts) != 3 {
  1159  		t.Fatal("should have returned 3 posts")
  1160  	}
  1161  
  1162  	if !reflect.DeepEqual(rpl.Posts, opl2.Posts) {
  1163  		t.Fatal("posts should have matched")
  1164  	}
  1165  
  1166  	_, resp = Client.GetFlaggedPostsForUser("junk", 0, 10)
  1167  	CheckBadRequestStatus(t, resp)
  1168  
  1169  	_, resp = Client.GetFlaggedPostsForUser(GenerateTestId(), 0, 10)
  1170  	CheckForbiddenStatus(t, resp)
  1171  
  1172  	Client.Logout()
  1173  
  1174  	_, resp = Client.GetFlaggedPostsForUserInChannel(user.Id, channel1.Id, 0, 10)
  1175  	CheckUnauthorizedStatus(t, resp)
  1176  
  1177  	_, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 0, 10)
  1178  	CheckUnauthorizedStatus(t, resp)
  1179  
  1180  	_, resp = Client.GetFlaggedPostsForUser(user.Id, 0, 10)
  1181  	CheckUnauthorizedStatus(t, resp)
  1182  
  1183  	_, resp = th.SystemAdminClient.GetFlaggedPostsForUserInChannel(user.Id, channel1.Id, 0, 10)
  1184  	CheckNoError(t, resp)
  1185  
  1186  	_, resp = th.SystemAdminClient.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 0, 10)
  1187  	CheckNoError(t, resp)
  1188  
  1189  	_, resp = th.SystemAdminClient.GetFlaggedPostsForUser(user.Id, 0, 10)
  1190  	CheckNoError(t, resp)
  1191  }
  1192  
  1193  func TestGetPostsAfterAndBefore(t *testing.T) {
  1194  	th := Setup().InitBasic()
  1195  	defer th.TearDown()
  1196  	Client := th.Client
  1197  
  1198  	post1 := th.CreatePost()
  1199  	post2 := th.CreatePost()
  1200  	post3 := th.CreatePost()
  1201  	post4 := th.CreatePost()
  1202  	post5 := th.CreatePost()
  1203  
  1204  	posts, resp := Client.GetPostsBefore(th.BasicChannel.Id, post3.Id, 0, 100, "")
  1205  	CheckNoError(t, resp)
  1206  
  1207  	found := make([]bool, 2)
  1208  	for _, p := range posts.Posts {
  1209  		if p.Id == post1.Id {
  1210  			found[0] = true
  1211  		} else if p.Id == post2.Id {
  1212  			found[1] = true
  1213  		}
  1214  
  1215  		if p.Id == post4.Id || p.Id == post5.Id {
  1216  			t.Fatal("returned posts after")
  1217  		}
  1218  	}
  1219  
  1220  	for _, f := range found {
  1221  		if !f {
  1222  			t.Fatal("missing post")
  1223  		}
  1224  	}
  1225  
  1226  	posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post3.Id, 1, 1, "")
  1227  	CheckNoError(t, resp)
  1228  
  1229  	if len(posts.Posts) != 1 {
  1230  		t.Fatal("too many posts returned")
  1231  	}
  1232  
  1233  	posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, "junk", 1, 1, "")
  1234  	CheckNoError(t, resp)
  1235  
  1236  	if len(posts.Posts) != 0 {
  1237  		t.Fatal("should have no posts")
  1238  	}
  1239  
  1240  	posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post3.Id, 0, 100, "")
  1241  	CheckNoError(t, resp)
  1242  
  1243  	found = make([]bool, 2)
  1244  	for _, p := range posts.Posts {
  1245  		if p.Id == post4.Id {
  1246  			found[0] = true
  1247  		} else if p.Id == post5.Id {
  1248  			found[1] = true
  1249  		}
  1250  
  1251  		if p.Id == post1.Id || p.Id == post2.Id {
  1252  			t.Fatal("returned posts before")
  1253  		}
  1254  	}
  1255  
  1256  	for _, f := range found {
  1257  		if !f {
  1258  			t.Fatal("missing post")
  1259  		}
  1260  	}
  1261  
  1262  	posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post3.Id, 1, 1, "")
  1263  	CheckNoError(t, resp)
  1264  
  1265  	if len(posts.Posts) != 1 {
  1266  		t.Fatal("too many posts returned")
  1267  	}
  1268  
  1269  	posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, "junk", 1, 1, "")
  1270  	CheckNoError(t, resp)
  1271  
  1272  	if len(posts.Posts) != 0 {
  1273  		t.Fatal("should have no posts")
  1274  	}
  1275  }
  1276  
  1277  func TestGetPost(t *testing.T) {
  1278  	th := Setup().InitBasic()
  1279  	defer th.TearDown()
  1280  	Client := th.Client
  1281  
  1282  	post, resp := Client.GetPost(th.BasicPost.Id, "")
  1283  	CheckNoError(t, resp)
  1284  
  1285  	if post.Id != th.BasicPost.Id {
  1286  		t.Fatal("post ids don't match")
  1287  	}
  1288  
  1289  	post, resp = Client.GetPost(th.BasicPost.Id, resp.Etag)
  1290  	CheckEtag(t, post, resp)
  1291  
  1292  	_, resp = Client.GetPost("", "")
  1293  	CheckNotFoundStatus(t, resp)
  1294  
  1295  	_, resp = Client.GetPost("junk", "")
  1296  	CheckBadRequestStatus(t, resp)
  1297  
  1298  	_, resp = Client.GetPost(model.NewId(), "")
  1299  	CheckNotFoundStatus(t, resp)
  1300  
  1301  	Client.RemoveUserFromChannel(th.BasicChannel.Id, th.BasicUser.Id)
  1302  
  1303  	// Channel is public, should be able to read post
  1304  	_, resp = Client.GetPost(th.BasicPost.Id, "")
  1305  	CheckNoError(t, resp)
  1306  
  1307  	privatePost := th.CreatePostWithClient(Client, th.BasicPrivateChannel)
  1308  
  1309  	_, resp = Client.GetPost(privatePost.Id, "")
  1310  	CheckNoError(t, resp)
  1311  
  1312  	Client.RemoveUserFromChannel(th.BasicPrivateChannel.Id, th.BasicUser.Id)
  1313  
  1314  	// Channel is private, should not be able to read post
  1315  	_, resp = Client.GetPost(privatePost.Id, "")
  1316  	CheckForbiddenStatus(t, resp)
  1317  
  1318  	Client.Logout()
  1319  	_, resp = Client.GetPost(model.NewId(), "")
  1320  	CheckUnauthorizedStatus(t, resp)
  1321  
  1322  	_, resp = th.SystemAdminClient.GetPost(th.BasicPost.Id, "")
  1323  	CheckNoError(t, resp)
  1324  }
  1325  
  1326  func TestDeletePost(t *testing.T) {
  1327  	th := Setup().InitBasic()
  1328  	defer th.TearDown()
  1329  	Client := th.Client
  1330  
  1331  	_, resp := Client.DeletePost("")
  1332  	CheckNotFoundStatus(t, resp)
  1333  
  1334  	_, resp = Client.DeletePost("junk")
  1335  	CheckBadRequestStatus(t, resp)
  1336  
  1337  	_, resp = Client.DeletePost(th.BasicPost.Id)
  1338  	CheckForbiddenStatus(t, resp)
  1339  
  1340  	Client.Login(th.TeamAdminUser.Email, th.TeamAdminUser.Password)
  1341  	_, resp = Client.DeletePost(th.BasicPost.Id)
  1342  	CheckNoError(t, resp)
  1343  
  1344  	post := th.CreatePost()
  1345  	user := th.CreateUser()
  1346  
  1347  	Client.Logout()
  1348  	Client.Login(user.Email, user.Password)
  1349  
  1350  	_, resp = Client.DeletePost(post.Id)
  1351  	CheckForbiddenStatus(t, resp)
  1352  
  1353  	Client.Logout()
  1354  	_, resp = Client.DeletePost(model.NewId())
  1355  	CheckUnauthorizedStatus(t, resp)
  1356  
  1357  	status, resp := th.SystemAdminClient.DeletePost(post.Id)
  1358  	if !status {
  1359  		t.Fatal("post should return status OK")
  1360  	}
  1361  	CheckNoError(t, resp)
  1362  }
  1363  
  1364  func TestGetPostThread(t *testing.T) {
  1365  	th := Setup().InitBasic()
  1366  	defer th.TearDown()
  1367  	Client := th.Client
  1368  
  1369  	post := &model.Post{ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a", RootId: th.BasicPost.Id}
  1370  	post, _ = Client.CreatePost(post)
  1371  
  1372  	list, resp := Client.GetPostThread(th.BasicPost.Id, "")
  1373  	CheckNoError(t, resp)
  1374  
  1375  	var list2 *model.PostList
  1376  	list2, resp = Client.GetPostThread(th.BasicPost.Id, resp.Etag)
  1377  	CheckEtag(t, list2, resp)
  1378  
  1379  	if list.Order[0] != th.BasicPost.Id {
  1380  		t.Fatal("wrong order")
  1381  	}
  1382  
  1383  	if _, ok := list.Posts[th.BasicPost.Id]; !ok {
  1384  		t.Fatal("should have had post")
  1385  	}
  1386  
  1387  	if _, ok := list.Posts[post.Id]; !ok {
  1388  		t.Fatal("should have had post")
  1389  	}
  1390  
  1391  	_, resp = Client.GetPostThread("junk", "")
  1392  	CheckBadRequestStatus(t, resp)
  1393  
  1394  	_, resp = Client.GetPostThread(model.NewId(), "")
  1395  	CheckNotFoundStatus(t, resp)
  1396  
  1397  	Client.RemoveUserFromChannel(th.BasicChannel.Id, th.BasicUser.Id)
  1398  
  1399  	// Channel is public, should be able to read post
  1400  	_, resp = Client.GetPostThread(th.BasicPost.Id, "")
  1401  	CheckNoError(t, resp)
  1402  
  1403  	privatePost := th.CreatePostWithClient(Client, th.BasicPrivateChannel)
  1404  
  1405  	_, resp = Client.GetPostThread(privatePost.Id, "")
  1406  	CheckNoError(t, resp)
  1407  
  1408  	Client.RemoveUserFromChannel(th.BasicPrivateChannel.Id, th.BasicUser.Id)
  1409  
  1410  	// Channel is private, should not be able to read post
  1411  	_, resp = Client.GetPostThread(privatePost.Id, "")
  1412  	CheckForbiddenStatus(t, resp)
  1413  
  1414  	Client.Logout()
  1415  	_, resp = Client.GetPostThread(model.NewId(), "")
  1416  	CheckUnauthorizedStatus(t, resp)
  1417  
  1418  	_, resp = th.SystemAdminClient.GetPostThread(th.BasicPost.Id, "")
  1419  	CheckNoError(t, resp)
  1420  }
  1421  
  1422  func TestSearchPosts(t *testing.T) {
  1423  	th := Setup().InitBasic()
  1424  	defer th.TearDown()
  1425  	experimentalViewArchivedChannels := *th.App.Config().TeamSettings.ExperimentalViewArchivedChannels
  1426  	defer func() {
  1427  		th.App.UpdateConfig(func(cfg *model.Config) {
  1428  			cfg.TeamSettings.ExperimentalViewArchivedChannels = &experimentalViewArchivedChannels
  1429  		})
  1430  	}()
  1431  	th.App.UpdateConfig(func(cfg *model.Config) {
  1432  		*cfg.TeamSettings.ExperimentalViewArchivedChannels = true
  1433  	})
  1434  
  1435  	th.LoginBasic()
  1436  	Client := th.Client
  1437  
  1438  	message := "search for post1"
  1439  	_ = th.CreateMessagePost(message)
  1440  
  1441  	message = "search for post2"
  1442  	post2 := th.CreateMessagePost(message)
  1443  
  1444  	message = "#hashtag search for post3"
  1445  	post3 := th.CreateMessagePost(message)
  1446  
  1447  	message = "hashtag for post4"
  1448  	_ = th.CreateMessagePost(message)
  1449  
  1450  	archivedChannel := th.CreatePublicChannel()
  1451  	_ = th.CreateMessagePostWithClient(th.Client, archivedChannel, "#hashtag for post3")
  1452  	th.Client.DeleteChannel(archivedChannel.Id)
  1453  
  1454  	terms := "search"
  1455  	isOrSearch := false
  1456  	timezoneOffset := 5
  1457  	searchParams := model.SearchParameter{
  1458  		Terms:          &terms,
  1459  		IsOrSearch:     &isOrSearch,
  1460  		TimeZoneOffset: &timezoneOffset,
  1461  	}
  1462  	posts, resp := Client.SearchPostsWithParams(th.BasicTeam.Id, &searchParams)
  1463  	CheckNoError(t, resp)
  1464  	if len(posts.Order) != 3 {
  1465  		t.Fatal("wrong search")
  1466  	}
  1467  
  1468  	terms = "search"
  1469  	page := 0
  1470  	perPage := 2
  1471  	searchParams = model.SearchParameter{
  1472  		Terms:          &terms,
  1473  		IsOrSearch:     &isOrSearch,
  1474  		TimeZoneOffset: &timezoneOffset,
  1475  		Page:           &page,
  1476  		PerPage:        &perPage,
  1477  	}
  1478  	posts2, resp := Client.SearchPostsWithParams(th.BasicTeam.Id, &searchParams)
  1479  	CheckNoError(t, resp)
  1480  	if len(posts2.Order) != 3 { // We don't support paging for DB search yet, modify this when we do.
  1481  		t.Fatal("Wrong number of posts", len(posts2.Order))
  1482  	}
  1483  	assert.Equal(t, posts.Order[0], posts2.Order[0])
  1484  	assert.Equal(t, posts.Order[1], posts2.Order[1])
  1485  
  1486  	page = 1
  1487  	searchParams = model.SearchParameter{
  1488  		Terms:          &terms,
  1489  		IsOrSearch:     &isOrSearch,
  1490  		TimeZoneOffset: &timezoneOffset,
  1491  		Page:           &page,
  1492  		PerPage:        &perPage,
  1493  	}
  1494  	posts2, resp = Client.SearchPostsWithParams(th.BasicTeam.Id, &searchParams)
  1495  	CheckNoError(t, resp)
  1496  	if len(posts2.Order) != 0 { // We don't support paging for DB search yet, modify this when we do.
  1497  		t.Fatal("Wrong number of posts", len(posts2.Order))
  1498  	}
  1499  
  1500  	posts, resp = Client.SearchPosts(th.BasicTeam.Id, "search", false)
  1501  	CheckNoError(t, resp)
  1502  	if len(posts.Order) != 3 {
  1503  		t.Fatal("wrong search")
  1504  	}
  1505  
  1506  	posts, resp = Client.SearchPosts(th.BasicTeam.Id, "post2", false)
  1507  	CheckNoError(t, resp)
  1508  	if len(posts.Order) != 1 && posts.Order[0] == post2.Id {
  1509  		t.Fatal("wrong search")
  1510  	}
  1511  
  1512  	posts, resp = Client.SearchPosts(th.BasicTeam.Id, "#hashtag", false)
  1513  	CheckNoError(t, resp)
  1514  	if len(posts.Order) != 1 && posts.Order[0] == post3.Id {
  1515  		t.Fatal("wrong search")
  1516  	}
  1517  
  1518  	terms = "#hashtag"
  1519  	includeDeletedChannels := true
  1520  	searchParams = model.SearchParameter{
  1521  		Terms:                  &terms,
  1522  		IsOrSearch:             &isOrSearch,
  1523  		TimeZoneOffset:         &timezoneOffset,
  1524  		IncludeDeletedChannels: &includeDeletedChannels,
  1525  	}
  1526  	posts, resp = Client.SearchPostsWithParams(th.BasicTeam.Id, &searchParams)
  1527  	CheckNoError(t, resp)
  1528  	if len(posts.Order) != 2 {
  1529  		t.Fatal("wrong search")
  1530  	}
  1531  
  1532  	th.App.UpdateConfig(func(cfg *model.Config) {
  1533  		*cfg.TeamSettings.ExperimentalViewArchivedChannels = false
  1534  	})
  1535  
  1536  	posts, resp = Client.SearchPostsWithParams(th.BasicTeam.Id, &searchParams)
  1537  	CheckNoError(t, resp)
  1538  	if len(posts.Order) != 1 {
  1539  		t.Fatal("wrong search")
  1540  	}
  1541  
  1542  	if posts, _ = Client.SearchPosts(th.BasicTeam.Id, "*", false); len(posts.Order) != 0 {
  1543  		t.Fatal("searching for just * shouldn't return any results")
  1544  	}
  1545  
  1546  	posts, resp = Client.SearchPosts(th.BasicTeam.Id, "post1 post2", true)
  1547  	CheckNoError(t, resp)
  1548  
  1549  	if len(posts.Order) != 2 {
  1550  		t.Fatal("wrong search results")
  1551  	}
  1552  
  1553  	_, resp = Client.SearchPosts("junk", "#sgtitlereview", false)
  1554  	CheckBadRequestStatus(t, resp)
  1555  
  1556  	_, resp = Client.SearchPosts(model.NewId(), "#sgtitlereview", false)
  1557  	CheckForbiddenStatus(t, resp)
  1558  
  1559  	_, resp = Client.SearchPosts(th.BasicTeam.Id, "", false)
  1560  	CheckBadRequestStatus(t, resp)
  1561  
  1562  	Client.Logout()
  1563  	_, resp = Client.SearchPosts(th.BasicTeam.Id, "#sgtitlereview", false)
  1564  	CheckUnauthorizedStatus(t, resp)
  1565  }
  1566  
  1567  func TestSearchHashtagPosts(t *testing.T) {
  1568  	th := Setup().InitBasic()
  1569  	defer th.TearDown()
  1570  	th.LoginBasic()
  1571  	Client := th.Client
  1572  
  1573  	message := "#sgtitlereview with space"
  1574  	assert.NotNil(t, th.CreateMessagePost(message))
  1575  
  1576  	message = "#sgtitlereview\n with return"
  1577  	assert.NotNil(t, th.CreateMessagePost(message))
  1578  
  1579  	message = "no hashtag"
  1580  	assert.NotNil(t, th.CreateMessagePost(message))
  1581  
  1582  	posts, resp := Client.SearchPosts(th.BasicTeam.Id, "#sgtitlereview", false)
  1583  	CheckNoError(t, resp)
  1584  	if len(posts.Order) != 2 {
  1585  		t.Fatal("wrong search results")
  1586  	}
  1587  
  1588  	Client.Logout()
  1589  	_, resp = Client.SearchPosts(th.BasicTeam.Id, "#sgtitlereview", false)
  1590  	CheckUnauthorizedStatus(t, resp)
  1591  }
  1592  
  1593  func TestSearchPostsInChannel(t *testing.T) {
  1594  	th := Setup().InitBasic()
  1595  	defer th.TearDown()
  1596  	th.LoginBasic()
  1597  	Client := th.Client
  1598  
  1599  	channel := th.CreatePublicChannel()
  1600  
  1601  	message := "sgtitlereview with space"
  1602  	_ = th.CreateMessagePost(message)
  1603  
  1604  	message = "sgtitlereview\n with return"
  1605  	_ = th.CreateMessagePostWithClient(Client, th.BasicChannel2, message)
  1606  
  1607  	message = "other message with no return"
  1608  	_ = th.CreateMessagePostWithClient(Client, th.BasicChannel2, message)
  1609  
  1610  	message = "other message with no return"
  1611  	_ = th.CreateMessagePostWithClient(Client, channel, message)
  1612  
  1613  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "channel:", false); len(posts.Order) != 0 {
  1614  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1615  	}
  1616  
  1617  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "in:", false); len(posts.Order) != 0 {
  1618  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1619  	}
  1620  
  1621  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "channel:"+th.BasicChannel.Name, false); len(posts.Order) != 2 {
  1622  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1623  	}
  1624  
  1625  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "in:"+th.BasicChannel2.Name, false); len(posts.Order) != 2 {
  1626  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1627  	}
  1628  
  1629  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "channel:"+th.BasicChannel2.Name, false); len(posts.Order) != 2 {
  1630  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1631  	}
  1632  
  1633  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "ChAnNeL:"+th.BasicChannel2.Name, false); len(posts.Order) != 2 {
  1634  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1635  	}
  1636  
  1637  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "sgtitlereview", false); len(posts.Order) != 2 {
  1638  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1639  	}
  1640  
  1641  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "sgtitlereview channel:"+th.BasicChannel.Name, false); len(posts.Order) != 1 {
  1642  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1643  	}
  1644  
  1645  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "sgtitlereview in: "+th.BasicChannel2.Name, false); len(posts.Order) != 1 {
  1646  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1647  	}
  1648  
  1649  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "sgtitlereview channel: "+th.BasicChannel2.Name, false); len(posts.Order) != 1 {
  1650  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1651  	}
  1652  
  1653  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "channel: "+th.BasicChannel2.Name+" channel: "+channel.Name, false); len(posts.Order) != 3 {
  1654  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1655  	}
  1656  
  1657  }
  1658  
  1659  func TestSearchPostsFromUser(t *testing.T) {
  1660  	th := Setup().InitBasic()
  1661  	defer th.TearDown()
  1662  	Client := th.Client
  1663  
  1664  	th.LoginTeamAdmin()
  1665  	user := th.CreateUser()
  1666  	th.LinkUserToTeam(user, th.BasicTeam)
  1667  	th.App.AddUserToChannel(user, th.BasicChannel)
  1668  	th.App.AddUserToChannel(user, th.BasicChannel2)
  1669  
  1670  	message := "sgtitlereview with space"
  1671  	_ = th.CreateMessagePost(message)
  1672  
  1673  	Client.Logout()
  1674  	th.LoginBasic2()
  1675  
  1676  	message = "sgtitlereview\n with return"
  1677  	_ = th.CreateMessagePostWithClient(Client, th.BasicChannel2, message)
  1678  
  1679  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.TeamAdminUser.Username, false); len(posts.Order) != 2 {
  1680  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1681  	}
  1682  
  1683  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username, false); len(posts.Order) != 1 {
  1684  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1685  	}
  1686  
  1687  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username+" sgtitlereview", false); len(posts.Order) != 1 {
  1688  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1689  	}
  1690  
  1691  	message = "hullo"
  1692  	_ = th.CreateMessagePost(message)
  1693  
  1694  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username+" in:"+th.BasicChannel.Name, false); len(posts.Order) != 1 {
  1695  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1696  	}
  1697  
  1698  	Client.Login(user.Email, user.Password)
  1699  
  1700  	// wait for the join/leave messages to be created for user3 since they're done asynchronously
  1701  	time.Sleep(100 * time.Millisecond)
  1702  
  1703  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username, false); len(posts.Order) != 2 {
  1704  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1705  	}
  1706  
  1707  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username+" from: "+user.Username, false); len(posts.Order) != 2 {
  1708  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1709  	}
  1710  
  1711  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username+" from: "+user.Username+" in:"+th.BasicChannel2.Name, false); len(posts.Order) != 1 {
  1712  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1713  	}
  1714  
  1715  	message = "coconut"
  1716  	_ = th.CreateMessagePostWithClient(Client, th.BasicChannel2, message)
  1717  
  1718  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username+" from: "+user.Username+" in:"+th.BasicChannel2.Name+" coconut", false); len(posts.Order) != 1 {
  1719  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1720  	}
  1721  }
  1722  
  1723  func TestSearchPostsWithDateFlags(t *testing.T) {
  1724  	th := Setup().InitBasic()
  1725  	defer th.TearDown()
  1726  	th.LoginBasic()
  1727  	Client := th.Client
  1728  
  1729  	message := "sgtitlereview\n with return"
  1730  	createDate := time.Date(2018, 8, 1, 5, 0, 0, 0, time.UTC)
  1731  	_ = th.CreateMessagePostNoClient(th.BasicChannel, message, utils.MillisFromTime(createDate))
  1732  
  1733  	message = "other message with no return"
  1734  	createDate = time.Date(2018, 8, 2, 5, 0, 0, 0, time.UTC)
  1735  	_ = th.CreateMessagePostNoClient(th.BasicChannel, message, utils.MillisFromTime(createDate))
  1736  
  1737  	message = "other message with no return"
  1738  	createDate = time.Date(2018, 8, 3, 5, 0, 0, 0, time.UTC)
  1739  	_ = th.CreateMessagePostNoClient(th.BasicChannel, message, utils.MillisFromTime(createDate))
  1740  
  1741  	posts, _ := Client.SearchPosts(th.BasicTeam.Id, "return", false)
  1742  	if len(posts.Order) != 3 {
  1743  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1744  	}
  1745  
  1746  	posts, _ = Client.SearchPosts(th.BasicTeam.Id, "on:", false)
  1747  	if len(posts.Order) != 0 {
  1748  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1749  	}
  1750  
  1751  	posts, _ = Client.SearchPosts(th.BasicTeam.Id, "after:", false)
  1752  	if len(posts.Order) != 0 {
  1753  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1754  	}
  1755  
  1756  	posts, _ = Client.SearchPosts(th.BasicTeam.Id, "before:", false)
  1757  	if len(posts.Order) != 0 {
  1758  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1759  	}
  1760  
  1761  	posts, _ = Client.SearchPosts(th.BasicTeam.Id, "on:2018-08-01", false)
  1762  	if len(posts.Order) != 1 {
  1763  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1764  	}
  1765  
  1766  	posts, _ = Client.SearchPosts(th.BasicTeam.Id, "after:2018-08-01", false)
  1767  	resultCount := 0
  1768  	for _, post := range posts.Posts {
  1769  		if post.UserId == th.BasicUser.Id {
  1770  			resultCount = resultCount + 1
  1771  		}
  1772  	}
  1773  	if resultCount != 2 {
  1774  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1775  	}
  1776  
  1777  	posts, _ = Client.SearchPosts(th.BasicTeam.Id, "before:2018-08-02", false)
  1778  	if len(posts.Order) != 1 {
  1779  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1780  	}
  1781  
  1782  	posts, _ = Client.SearchPosts(th.BasicTeam.Id, "before:2018-08-03 after:2018-08-02", false)
  1783  	if len(posts.Order) != 0 {
  1784  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1785  	}
  1786  
  1787  	posts, _ = Client.SearchPosts(th.BasicTeam.Id, "before:2018-08-03 after:2018-08-01", false)
  1788  	if len(posts.Order) != 1 {
  1789  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1790  	}
  1791  }
  1792  
  1793  func TestGetFileInfosForPost(t *testing.T) {
  1794  	th := Setup().InitBasic()
  1795  	defer th.TearDown()
  1796  	Client := th.Client
  1797  
  1798  	fileIds := make([]string, 3)
  1799  	if data, err := testutils.ReadTestFile("test.png"); err != nil {
  1800  		t.Fatal(err)
  1801  	} else {
  1802  		for i := 0; i < 3; i++ {
  1803  			fileResp, _ := Client.UploadFile(data, th.BasicChannel.Id, "test.png")
  1804  			fileIds[i] = fileResp.FileInfos[0].Id
  1805  		}
  1806  	}
  1807  
  1808  	post := &model.Post{ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a", FileIds: fileIds}
  1809  	post, _ = Client.CreatePost(post)
  1810  
  1811  	infos, resp := Client.GetFileInfosForPost(post.Id, "")
  1812  	CheckNoError(t, resp)
  1813  
  1814  	if len(infos) != 3 {
  1815  		t.Fatal("missing file infos")
  1816  	}
  1817  
  1818  	found := false
  1819  	for _, info := range infos {
  1820  		if info.Id == fileIds[0] {
  1821  			found = true
  1822  		}
  1823  	}
  1824  
  1825  	if !found {
  1826  		t.Fatal("missing file info")
  1827  	}
  1828  
  1829  	infos, resp = Client.GetFileInfosForPost(post.Id, resp.Etag)
  1830  	CheckEtag(t, infos, resp)
  1831  
  1832  	infos, resp = Client.GetFileInfosForPost(th.BasicPost.Id, "")
  1833  	CheckNoError(t, resp)
  1834  
  1835  	if len(infos) != 0 {
  1836  		t.Fatal("should have no file infos")
  1837  	}
  1838  
  1839  	_, resp = Client.GetFileInfosForPost("junk", "")
  1840  	CheckBadRequestStatus(t, resp)
  1841  
  1842  	_, resp = Client.GetFileInfosForPost(model.NewId(), "")
  1843  	CheckForbiddenStatus(t, resp)
  1844  
  1845  	Client.Logout()
  1846  	_, resp = Client.GetFileInfosForPost(model.NewId(), "")
  1847  	CheckUnauthorizedStatus(t, resp)
  1848  
  1849  	_, resp = th.SystemAdminClient.GetFileInfosForPost(th.BasicPost.Id, "")
  1850  	CheckNoError(t, resp)
  1851  }