github.com/demisto/mattermost-server@v4.9.0-rc3+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/mattermost/mattermost-server/app"
    19  	"github.com/mattermost/mattermost-server/model"
    20  )
    21  
    22  func TestCreatePost(t *testing.T) {
    23  	th := Setup().InitBasic().InitSystemAdmin()
    24  	defer th.TearDown()
    25  	Client := th.Client
    26  
    27  	post := &model.Post{ChannelId: th.BasicChannel.Id, Message: "#hashtag a" + model.NewId() + "a", Props: model.StringInterface{model.PROPS_ADD_CHANNEL_MEMBER: "no good"}}
    28  	rpost, resp := Client.CreatePost(post)
    29  	CheckNoError(t, resp)
    30  	CheckCreatedStatus(t, resp)
    31  
    32  	if rpost.Message != post.Message {
    33  		t.Fatal("message didn't match")
    34  	}
    35  
    36  	if rpost.Hashtags != "#hashtag" {
    37  		t.Fatal("hashtag didn't match")
    38  	}
    39  
    40  	if len(rpost.FileIds) != 0 {
    41  		t.Fatal("shouldn't have files")
    42  	}
    43  
    44  	if rpost.EditAt != 0 {
    45  		t.Fatal("newly created post shouldn't have EditAt set")
    46  	}
    47  
    48  	if rpost.Props[model.PROPS_ADD_CHANNEL_MEMBER] != nil {
    49  		t.Fatal("newly created post shouldn't have Props['add_channel_member'] set")
    50  	}
    51  
    52  	post.RootId = rpost.Id
    53  	post.ParentId = rpost.Id
    54  	_, resp = Client.CreatePost(post)
    55  	CheckNoError(t, resp)
    56  
    57  	post.RootId = "junk"
    58  	_, resp = Client.CreatePost(post)
    59  	CheckBadRequestStatus(t, resp)
    60  
    61  	post.RootId = rpost.Id
    62  	post.ParentId = "junk"
    63  	_, resp = Client.CreatePost(post)
    64  	CheckBadRequestStatus(t, resp)
    65  
    66  	post2 := &model.Post{ChannelId: th.BasicChannel2.Id, Message: "zz" + model.NewId() + "a", CreateAt: 123}
    67  	rpost2, resp := Client.CreatePost(post2)
    68  
    69  	if rpost2.CreateAt == post2.CreateAt {
    70  		t.Fatal("create at should not match")
    71  	}
    72  
    73  	post.RootId = ""
    74  	post.ParentId = ""
    75  	post.Type = model.POST_SYSTEM_GENERIC
    76  	_, resp = Client.CreatePost(post)
    77  	CheckBadRequestStatus(t, resp)
    78  
    79  	post.Type = ""
    80  	post.RootId = rpost2.Id
    81  	post.ParentId = rpost2.Id
    82  	_, resp = Client.CreatePost(post)
    83  	CheckBadRequestStatus(t, resp)
    84  
    85  	post.RootId = ""
    86  	post.ParentId = ""
    87  	post.ChannelId = "junk"
    88  	_, resp = Client.CreatePost(post)
    89  	CheckForbiddenStatus(t, resp)
    90  
    91  	post.ChannelId = model.NewId()
    92  	_, resp = Client.CreatePost(post)
    93  	CheckForbiddenStatus(t, resp)
    94  
    95  	if r, err := Client.DoApiPost("/posts", "garbage"); err == nil {
    96  		t.Fatal("should have errored")
    97  	} else {
    98  		if r.StatusCode != http.StatusBadRequest {
    99  			t.Log("actual: " + strconv.Itoa(r.StatusCode))
   100  			t.Log("expected: " + strconv.Itoa(http.StatusBadRequest))
   101  			t.Fatal("wrong status code")
   102  		}
   103  	}
   104  
   105  	Client.Logout()
   106  	_, resp = Client.CreatePost(post)
   107  	CheckUnauthorizedStatus(t, resp)
   108  
   109  	post.ChannelId = th.BasicChannel.Id
   110  	post.CreateAt = 123
   111  	rpost, resp = th.SystemAdminClient.CreatePost(post)
   112  	CheckNoError(t, resp)
   113  
   114  	if rpost.CreateAt != post.CreateAt {
   115  		t.Fatal("create at should match")
   116  	}
   117  }
   118  
   119  func testCreatePostWithOutgoingHook(
   120  	t *testing.T,
   121  	hookContentType, expectedContentType, message, triggerWord string,
   122  	fileIds []string,
   123  	triggerWhen int,
   124  	commentPostType bool,
   125  ) {
   126  	th := Setup().InitBasic().InitSystemAdmin()
   127  	defer th.TearDown()
   128  	user := th.SystemAdminUser
   129  	team := th.BasicTeam
   130  	channel := th.BasicChannel
   131  
   132  	th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true })
   133  	th.App.UpdateConfig(func(cfg *model.Config) {
   134  		*cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost 127.0.0.1"
   135  	})
   136  
   137  	var hook *model.OutgoingWebhook
   138  	var post *model.Post
   139  
   140  	// Create a test server that is the target of the outgoing webhook. It will
   141  	// validate the webhook body fields and write to the success channel on
   142  	// success/failure.
   143  	success := make(chan bool)
   144  	wait := make(chan bool, 1)
   145  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   146  		<-wait
   147  
   148  		requestContentType := r.Header.Get("Content-Type")
   149  		if requestContentType != expectedContentType {
   150  			t.Logf("Content-Type is %s, should be %s", requestContentType, expectedContentType)
   151  			success <- false
   152  			return
   153  		}
   154  
   155  		expectedPayload := &model.OutgoingWebhookPayload{
   156  			Token:       hook.Token,
   157  			TeamId:      hook.TeamId,
   158  			TeamDomain:  team.Name,
   159  			ChannelId:   post.ChannelId,
   160  			ChannelName: channel.Name,
   161  			Timestamp:   post.CreateAt,
   162  			UserId:      post.UserId,
   163  			UserName:    user.Username,
   164  			PostId:      post.Id,
   165  			Text:        post.Message,
   166  			TriggerWord: triggerWord,
   167  			FileIds:     strings.Join(post.FileIds, ","),
   168  		}
   169  
   170  		// depending on the Content-Type, we expect to find a JSON or form encoded payload
   171  		if requestContentType == "application/json" {
   172  			decoder := json.NewDecoder(r.Body)
   173  			o := &model.OutgoingWebhookPayload{}
   174  			decoder.Decode(&o)
   175  
   176  			if !reflect.DeepEqual(expectedPayload, o) {
   177  				t.Logf("JSON payload is %+v, should be %+v", o, expectedPayload)
   178  				success <- false
   179  				return
   180  			}
   181  		} else {
   182  			err := r.ParseForm()
   183  			if err != nil {
   184  				t.Logf("Error parsing form: %q", err)
   185  				success <- false
   186  				return
   187  			}
   188  
   189  			expectedFormValues, _ := url.ParseQuery(expectedPayload.ToFormValues())
   190  
   191  			if !reflect.DeepEqual(expectedFormValues, r.Form) {
   192  				t.Logf("Form values are: %q\n, should be: %q\n", r.Form, expectedFormValues)
   193  				success <- false
   194  				return
   195  			}
   196  		}
   197  
   198  		respPostType := "" //if is empty or post will do a normal post.
   199  		if commentPostType {
   200  			respPostType = model.OUTGOING_HOOK_RESPONSE_TYPE_COMMENT
   201  		}
   202  
   203  		outGoingHookResponse := &model.OutgoingWebhookResponse{
   204  			Text:         model.NewString("some test text"),
   205  			Username:     "TestCommandServer",
   206  			IconURL:      "https://www.mattermost.org/wp-content/uploads/2016/04/icon.png",
   207  			Type:         "custom_as",
   208  			ResponseType: respPostType,
   209  		}
   210  
   211  		fmt.Fprintf(w, outGoingHookResponse.ToJson())
   212  		success <- true
   213  	}))
   214  	defer ts.Close()
   215  
   216  	// create an outgoing webhook, passing it the test server URL
   217  	var triggerWords []string
   218  	if triggerWord != "" {
   219  		triggerWords = []string{triggerWord}
   220  	}
   221  
   222  	hook = &model.OutgoingWebhook{
   223  		ChannelId:    channel.Id,
   224  		TeamId:       team.Id,
   225  		ContentType:  hookContentType,
   226  		TriggerWords: triggerWords,
   227  		TriggerWhen:  triggerWhen,
   228  		CallbackURLs: []string{ts.URL},
   229  	}
   230  
   231  	hook, resp := th.SystemAdminClient.CreateOutgoingWebhook(hook)
   232  	CheckNoError(t, resp)
   233  
   234  	// create a post to trigger the webhook
   235  	post = &model.Post{
   236  		ChannelId: channel.Id,
   237  		Message:   message,
   238  		FileIds:   fileIds,
   239  	}
   240  
   241  	post, resp = th.SystemAdminClient.CreatePost(post)
   242  	CheckNoError(t, resp)
   243  
   244  	wait <- true
   245  
   246  	// We wait for the test server to write to the success channel and we make
   247  	// the test fail if that doesn't happen before the timeout.
   248  	select {
   249  	case ok := <-success:
   250  		if !ok {
   251  			t.Fatal("Test server did send an invalid webhook.")
   252  		}
   253  	case <-time.After(time.Second):
   254  		t.Fatal("Timeout, test server did not send the webhook.")
   255  	}
   256  
   257  	if commentPostType {
   258  		time.Sleep(time.Millisecond * 100)
   259  		postList, resp := th.SystemAdminClient.GetPostThread(post.Id, "")
   260  		CheckNoError(t, resp)
   261  		if postList.Order[0] != post.Id {
   262  			t.Fatal("wrong order")
   263  		}
   264  
   265  		if _, ok := postList.Posts[post.Id]; !ok {
   266  			t.Fatal("should have had post")
   267  		}
   268  
   269  		if len(postList.Posts) != 2 {
   270  			t.Fatal("should have 2 posts")
   271  		}
   272  
   273  	}
   274  }
   275  
   276  func TestCreatePostWithOutgoingHook_form_urlencoded(t *testing.T) {
   277  	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)
   278  	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)
   279  	testCreatePostWithOutgoingHook(t, "application/x-www-form-urlencoded", "application/x-www-form-urlencoded", "", "", []string{"file_id_1"}, app.TRIGGERWORDS_EXACT_MATCH, false)
   280  	testCreatePostWithOutgoingHook(t, "application/x-www-form-urlencoded", "application/x-www-form-urlencoded", "", "", []string{"file_id_1"}, app.TRIGGERWORDS_STARTS_WITH, false)
   281  	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)
   282  	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)
   283  }
   284  
   285  func TestCreatePostWithOutgoingHook_json(t *testing.T) {
   286  	testCreatePostWithOutgoingHook(t, "application/json", "application/json", "triggerword lorem ipsum", "triggerword", []string{"file_id_1, file_id_2"}, app.TRIGGERWORDS_EXACT_MATCH, false)
   287  	testCreatePostWithOutgoingHook(t, "application/json", "application/json", "triggerwordaaazzz lorem ipsum", "triggerword", []string{"file_id_1, file_id_2"}, app.TRIGGERWORDS_STARTS_WITH, false)
   288  	testCreatePostWithOutgoingHook(t, "application/json", "application/json", "triggerword lorem ipsum", "", []string{"file_id_1"}, app.TRIGGERWORDS_EXACT_MATCH, false)
   289  	testCreatePostWithOutgoingHook(t, "application/json", "application/json", "triggerwordaaazzz lorem ipsum", "", []string{"file_id_1"}, app.TRIGGERWORDS_STARTS_WITH, false)
   290  	testCreatePostWithOutgoingHook(t, "application/json", "application/json", "triggerword lorem ipsum", "triggerword", []string{"file_id_1, file_id_2"}, app.TRIGGERWORDS_EXACT_MATCH, true)
   291  	testCreatePostWithOutgoingHook(t, "application/json", "application/json", "triggerwordaaazzz lorem ipsum", "", []string{"file_id_1"}, app.TRIGGERWORDS_STARTS_WITH, true)
   292  }
   293  
   294  // hooks created before we added the ContentType field should be considered as
   295  // application/x-www-form-urlencoded
   296  func TestCreatePostWithOutgoingHook_no_content_type(t *testing.T) {
   297  	testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded", "triggerword lorem ipsum", "triggerword", []string{"file_id_1"}, app.TRIGGERWORDS_EXACT_MATCH, false)
   298  	testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded", "triggerwordaaazzz lorem ipsum", "triggerword", []string{"file_id_1"}, app.TRIGGERWORDS_STARTS_WITH, false)
   299  	testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded", "triggerword lorem ipsum", "", []string{"file_id_1, file_id_2"}, app.TRIGGERWORDS_EXACT_MATCH, false)
   300  	testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded", "triggerwordaaazzz lorem ipsum", "", []string{"file_id_1, file_id_2"}, app.TRIGGERWORDS_STARTS_WITH, false)
   301  	testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded", "triggerword lorem ipsum", "triggerword", []string{"file_id_1"}, app.TRIGGERWORDS_EXACT_MATCH, true)
   302  	testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded", "triggerword lorem ipsum", "", []string{"file_id_1, file_id_2"}, app.TRIGGERWORDS_EXACT_MATCH, true)
   303  }
   304  
   305  func TestCreatePostPublic(t *testing.T) {
   306  	th := Setup().InitBasic().InitSystemAdmin()
   307  	defer th.TearDown()
   308  	Client := th.Client
   309  
   310  	post := &model.Post{ChannelId: th.BasicChannel.Id, Message: "#hashtag a" + model.NewId() + "a"}
   311  
   312  	user := model.User{Email: th.GenerateTestEmail(), Nickname: "Joram Wilander", Password: "hello1", Username: GenerateTestUsername(), Roles: model.SYSTEM_USER_ROLE_ID}
   313  
   314  	ruser, resp := Client.CreateUser(&user)
   315  	CheckNoError(t, resp)
   316  
   317  	Client.Login(user.Email, user.Password)
   318  
   319  	_, resp = Client.CreatePost(post)
   320  	CheckForbiddenStatus(t, resp)
   321  
   322  	th.App.UpdateUserRoles(ruser.Id, model.SYSTEM_USER_ROLE_ID+" "+model.SYSTEM_POST_ALL_PUBLIC_ROLE_ID, false)
   323  	th.App.InvalidateAllCaches()
   324  
   325  	Client.Login(user.Email, user.Password)
   326  
   327  	_, resp = Client.CreatePost(post)
   328  	CheckNoError(t, resp)
   329  
   330  	post.ChannelId = th.BasicPrivateChannel.Id
   331  	_, resp = Client.CreatePost(post)
   332  	CheckForbiddenStatus(t, resp)
   333  
   334  	th.App.UpdateUserRoles(ruser.Id, model.SYSTEM_USER_ROLE_ID, false)
   335  	th.App.JoinUserToTeam(th.BasicTeam, ruser, "")
   336  	th.App.UpdateTeamMemberRoles(th.BasicTeam.Id, ruser.Id, model.TEAM_USER_ROLE_ID+" "+model.TEAM_POST_ALL_PUBLIC_ROLE_ID)
   337  	th.App.InvalidateAllCaches()
   338  
   339  	Client.Login(user.Email, user.Password)
   340  
   341  	post.ChannelId = th.BasicPrivateChannel.Id
   342  	_, resp = Client.CreatePost(post)
   343  	CheckForbiddenStatus(t, resp)
   344  
   345  	post.ChannelId = th.BasicChannel.Id
   346  	_, resp = Client.CreatePost(post)
   347  	CheckNoError(t, resp)
   348  }
   349  
   350  func TestCreatePostAll(t *testing.T) {
   351  	th := Setup().InitBasic().InitSystemAdmin()
   352  	defer th.TearDown()
   353  	Client := th.Client
   354  
   355  	post := &model.Post{ChannelId: th.BasicChannel.Id, Message: "#hashtag a" + model.NewId() + "a"}
   356  
   357  	user := model.User{Email: th.GenerateTestEmail(), Nickname: "Joram Wilander", Password: "hello1", Username: GenerateTestUsername(), Roles: model.SYSTEM_USER_ROLE_ID}
   358  
   359  	directChannel, _ := th.App.CreateDirectChannel(th.BasicUser.Id, th.BasicUser2.Id)
   360  
   361  	ruser, resp := Client.CreateUser(&user)
   362  	CheckNoError(t, resp)
   363  
   364  	Client.Login(user.Email, user.Password)
   365  
   366  	_, resp = Client.CreatePost(post)
   367  	CheckForbiddenStatus(t, resp)
   368  
   369  	th.App.UpdateUserRoles(ruser.Id, model.SYSTEM_USER_ROLE_ID+" "+model.SYSTEM_POST_ALL_ROLE_ID, false)
   370  	th.App.InvalidateAllCaches()
   371  
   372  	Client.Login(user.Email, user.Password)
   373  
   374  	_, resp = Client.CreatePost(post)
   375  	CheckNoError(t, resp)
   376  
   377  	post.ChannelId = th.BasicPrivateChannel.Id
   378  	_, resp = Client.CreatePost(post)
   379  	CheckNoError(t, resp)
   380  
   381  	post.ChannelId = directChannel.Id
   382  	_, resp = Client.CreatePost(post)
   383  	CheckNoError(t, resp)
   384  
   385  	th.App.UpdateUserRoles(ruser.Id, model.SYSTEM_USER_ROLE_ID, false)
   386  	th.App.JoinUserToTeam(th.BasicTeam, ruser, "")
   387  	th.App.UpdateTeamMemberRoles(th.BasicTeam.Id, ruser.Id, model.TEAM_USER_ROLE_ID+" "+model.TEAM_POST_ALL_ROLE_ID)
   388  	th.App.InvalidateAllCaches()
   389  
   390  	Client.Login(user.Email, user.Password)
   391  
   392  	post.ChannelId = th.BasicPrivateChannel.Id
   393  	_, resp = Client.CreatePost(post)
   394  	CheckNoError(t, resp)
   395  
   396  	post.ChannelId = th.BasicChannel.Id
   397  	_, resp = Client.CreatePost(post)
   398  	CheckNoError(t, resp)
   399  
   400  	post.ChannelId = directChannel.Id
   401  	_, resp = Client.CreatePost(post)
   402  	CheckForbiddenStatus(t, resp)
   403  }
   404  
   405  func TestCreatePostSendOutOfChannelMentions(t *testing.T) {
   406  	th := Setup().InitBasic().InitSystemAdmin()
   407  	defer th.TearDown()
   408  	Client := th.Client
   409  
   410  	WebSocketClient, err := th.CreateWebSocketClient()
   411  	if err != nil {
   412  		t.Fatal(err)
   413  	}
   414  	WebSocketClient.Listen()
   415  
   416  	inChannelUser := th.CreateUser()
   417  	th.LinkUserToTeam(inChannelUser, th.BasicTeam)
   418  	th.App.AddUserToChannel(inChannelUser, th.BasicChannel)
   419  
   420  	post1 := &model.Post{ChannelId: th.BasicChannel.Id, Message: "@" + inChannelUser.Username}
   421  	_, resp := Client.CreatePost(post1)
   422  	CheckNoError(t, resp)
   423  	CheckCreatedStatus(t, resp)
   424  
   425  	timeout := time.After(300 * time.Millisecond)
   426  	waiting := true
   427  	for waiting {
   428  		select {
   429  		case event := <-WebSocketClient.EventChannel:
   430  			if event.Event == model.WEBSOCKET_EVENT_EPHEMERAL_MESSAGE {
   431  				t.Fatal("should not have ephemeral message event")
   432  			}
   433  
   434  		case <-timeout:
   435  			waiting = false
   436  		}
   437  	}
   438  
   439  	outOfChannelUser := th.CreateUser()
   440  	th.LinkUserToTeam(outOfChannelUser, th.BasicTeam)
   441  
   442  	post2 := &model.Post{ChannelId: th.BasicChannel.Id, Message: "@" + outOfChannelUser.Username}
   443  	_, resp = Client.CreatePost(post2)
   444  	CheckNoError(t, resp)
   445  	CheckCreatedStatus(t, resp)
   446  
   447  	timeout = time.After(300 * time.Millisecond)
   448  	waiting = true
   449  	for waiting {
   450  		select {
   451  		case event := <-WebSocketClient.EventChannel:
   452  			if event.Event != model.WEBSOCKET_EVENT_EPHEMERAL_MESSAGE {
   453  				// Ignore any other events
   454  				continue
   455  			}
   456  
   457  			wpost := model.PostFromJson(strings.NewReader(event.Data["post"].(string)))
   458  			if acm, ok := wpost.Props[model.PROPS_ADD_CHANNEL_MEMBER].(map[string]interface{}); !ok {
   459  				t.Fatal("should have received ephemeral post with 'add_channel_member' in props")
   460  			} else {
   461  				if acm["post_id"] == nil || acm["user_ids"] == nil || acm["usernames"] == nil {
   462  					t.Fatal("should not be nil")
   463  				}
   464  			}
   465  			waiting = false
   466  		case <-timeout:
   467  			t.Fatal("timed out waiting for ephemeral message event")
   468  		}
   469  	}
   470  }
   471  
   472  func TestUpdatePost(t *testing.T) {
   473  	th := Setup().InitBasic().InitSystemAdmin()
   474  	defer th.TearDown()
   475  	Client := th.Client
   476  	channel := th.BasicChannel
   477  
   478  	th.App.SetLicense(model.NewTestLicense())
   479  
   480  	post := &model.Post{ChannelId: channel.Id, Message: "zz" + model.NewId() + "a"}
   481  	rpost, resp := Client.CreatePost(post)
   482  	CheckNoError(t, resp)
   483  
   484  	if rpost.Message != post.Message {
   485  		t.Fatal("full name didn't match")
   486  	}
   487  
   488  	if rpost.EditAt != 0 {
   489  		t.Fatal("Newly created post shouldn't have EditAt set")
   490  	}
   491  
   492  	msg := "zz" + model.NewId() + " update post"
   493  	rpost.Message = msg
   494  	rpost.UserId = ""
   495  
   496  	rupost, resp := Client.UpdatePost(rpost.Id, rpost)
   497  	CheckNoError(t, resp)
   498  
   499  	if rupost.Message != msg {
   500  		t.Fatal("failed to updates")
   501  	}
   502  	if rupost.EditAt == 0 {
   503  		t.Fatal("EditAt not updated for post")
   504  	}
   505  
   506  	msg1 := "#hashtag a" + model.NewId() + " update post again"
   507  	rpost.Message = msg1
   508  	rpost.Props[model.PROPS_ADD_CHANNEL_MEMBER] = "no good"
   509  	rrupost, resp := Client.UpdatePost(rpost.Id, rpost)
   510  	CheckNoError(t, resp)
   511  
   512  	if rrupost.Message != msg1 && rrupost.Hashtags != "#hashtag" {
   513  		t.Fatal("failed to updates")
   514  	}
   515  
   516  	if rrupost.Props[model.PROPS_ADD_CHANNEL_MEMBER] != nil {
   517  		t.Fatal("failed to sanitize Props['add_channel_member'], should be nil")
   518  	}
   519  
   520  	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)
   521  	if err != nil {
   522  		t.Fatal(err)
   523  	}
   524  
   525  	up2 := &model.Post{Id: rpost2.Id, ChannelId: channel.Id, Message: "zz" + model.NewId() + " update post 2"}
   526  	_, resp = Client.UpdatePost(rpost2.Id, up2)
   527  	CheckBadRequestStatus(t, resp)
   528  
   529  	Client.Logout()
   530  	_, resp = Client.UpdatePost(rpost.Id, rpost)
   531  	CheckUnauthorizedStatus(t, resp)
   532  
   533  	th.LoginBasic2()
   534  	_, resp = Client.UpdatePost(rpost.Id, rpost)
   535  	CheckForbiddenStatus(t, resp)
   536  
   537  	Client.Logout()
   538  
   539  	_, resp = th.SystemAdminClient.UpdatePost(rpost.Id, rpost)
   540  	CheckNoError(t, resp)
   541  }
   542  
   543  func TestPatchPost(t *testing.T) {
   544  	th := Setup().InitBasic().InitSystemAdmin()
   545  	defer th.TearDown()
   546  	Client := th.Client
   547  	channel := th.BasicChannel
   548  
   549  	th.App.SetLicense(model.NewTestLicense())
   550  
   551  	post := &model.Post{
   552  		ChannelId:    channel.Id,
   553  		IsPinned:     true,
   554  		Message:      "#hashtag a message",
   555  		Props:        model.StringInterface{"channel_header": "old_header"},
   556  		FileIds:      model.StringArray{"file1", "file2"},
   557  		HasReactions: true,
   558  	}
   559  	post, _ = Client.CreatePost(post)
   560  
   561  	patch := &model.PostPatch{}
   562  
   563  	patch.IsPinned = model.NewBool(false)
   564  	patch.Message = model.NewString("#otherhashtag other message")
   565  	patch.Props = new(model.StringInterface)
   566  	*patch.Props = model.StringInterface{"channel_header": "new_header"}
   567  	patch.FileIds = new(model.StringArray)
   568  	*patch.FileIds = model.StringArray{"file1", "otherfile2", "otherfile3"}
   569  	patch.HasReactions = model.NewBool(false)
   570  
   571  	rpost, resp := Client.PatchPost(post.Id, patch)
   572  	CheckNoError(t, resp)
   573  
   574  	if rpost.IsPinned {
   575  		t.Fatal("IsPinned did not update properly")
   576  	}
   577  	if rpost.Message != "#otherhashtag other message" {
   578  		t.Fatal("Message did not update properly")
   579  	}
   580  	if len(rpost.Props) != 1 {
   581  		t.Fatal("Props did not update properly")
   582  	}
   583  	if !reflect.DeepEqual(rpost.Props, *patch.Props) {
   584  		t.Fatal("Props did not update properly")
   585  	}
   586  	if rpost.Hashtags != "#otherhashtag" {
   587  		t.Fatal("Message did not update properly")
   588  	}
   589  	if len(rpost.FileIds) != 3 {
   590  		t.Fatal("FileIds did not update properly")
   591  	}
   592  	if !reflect.DeepEqual(rpost.FileIds, *patch.FileIds) {
   593  		t.Fatal("FileIds did not update properly")
   594  	}
   595  	if rpost.HasReactions {
   596  		t.Fatal("HasReactions did not update properly")
   597  	}
   598  
   599  	if r, err := Client.DoApiPut("/posts/"+post.Id+"/patch", "garbage"); err == nil {
   600  		t.Fatal("should have errored")
   601  	} else {
   602  		if r.StatusCode != http.StatusBadRequest {
   603  			t.Log("actual: " + strconv.Itoa(r.StatusCode))
   604  			t.Log("expected: " + strconv.Itoa(http.StatusBadRequest))
   605  			t.Fatal("wrong status code")
   606  		}
   607  	}
   608  
   609  	_, resp = Client.PatchPost("junk", patch)
   610  	CheckBadRequestStatus(t, resp)
   611  
   612  	_, resp = Client.PatchPost(GenerateTestId(), patch)
   613  	CheckForbiddenStatus(t, resp)
   614  
   615  	Client.Logout()
   616  	_, resp = Client.PatchPost(post.Id, patch)
   617  	CheckUnauthorizedStatus(t, resp)
   618  
   619  	th.LoginBasic2()
   620  	_, resp = Client.PatchPost(post.Id, patch)
   621  	CheckForbiddenStatus(t, resp)
   622  
   623  	th.LoginTeamAdmin()
   624  	_, resp = Client.PatchPost(post.Id, patch)
   625  	CheckNoError(t, resp)
   626  
   627  	_, resp = th.SystemAdminClient.PatchPost(post.Id, patch)
   628  	CheckNoError(t, resp)
   629  }
   630  
   631  func TestPinPost(t *testing.T) {
   632  	th := Setup().InitBasic().InitSystemAdmin()
   633  	defer th.TearDown()
   634  	Client := th.Client
   635  
   636  	post := th.BasicPost
   637  	pass, resp := Client.PinPost(post.Id)
   638  	CheckNoError(t, resp)
   639  
   640  	if !pass {
   641  		t.Fatal("should have passed")
   642  	}
   643  
   644  	if rpost, err := th.App.GetSinglePost(post.Id); err != nil && !rpost.IsPinned {
   645  		t.Fatal("failed to pin post")
   646  	}
   647  
   648  	pass, resp = Client.PinPost("junk")
   649  	CheckBadRequestStatus(t, resp)
   650  
   651  	if pass {
   652  		t.Fatal("should have failed")
   653  	}
   654  
   655  	_, resp = Client.PinPost(GenerateTestId())
   656  	CheckForbiddenStatus(t, resp)
   657  
   658  	Client.Logout()
   659  	_, resp = Client.PinPost(post.Id)
   660  	CheckUnauthorizedStatus(t, resp)
   661  
   662  	_, resp = th.SystemAdminClient.PinPost(post.Id)
   663  	CheckNoError(t, resp)
   664  }
   665  
   666  func TestUnpinPost(t *testing.T) {
   667  	th := Setup().InitBasic().InitSystemAdmin()
   668  	defer th.TearDown()
   669  	Client := th.Client
   670  
   671  	pinnedPost := th.CreatePinnedPost()
   672  	pass, resp := Client.UnpinPost(pinnedPost.Id)
   673  	CheckNoError(t, resp)
   674  
   675  	if !pass {
   676  		t.Fatal("should have passed")
   677  	}
   678  
   679  	if rpost, err := th.App.GetSinglePost(pinnedPost.Id); err != nil && rpost.IsPinned {
   680  		t.Fatal("failed to pin post")
   681  	}
   682  
   683  	pass, resp = Client.UnpinPost("junk")
   684  	CheckBadRequestStatus(t, resp)
   685  
   686  	if pass {
   687  		t.Fatal("should have failed")
   688  	}
   689  
   690  	_, resp = Client.UnpinPost(GenerateTestId())
   691  	CheckForbiddenStatus(t, resp)
   692  
   693  	Client.Logout()
   694  	_, resp = Client.UnpinPost(pinnedPost.Id)
   695  	CheckUnauthorizedStatus(t, resp)
   696  
   697  	_, resp = th.SystemAdminClient.UnpinPost(pinnedPost.Id)
   698  	CheckNoError(t, resp)
   699  }
   700  
   701  func TestGetPostsForChannel(t *testing.T) {
   702  	th := Setup().InitBasic().InitSystemAdmin()
   703  	defer th.TearDown()
   704  	Client := th.Client
   705  
   706  	post1 := th.CreatePost()
   707  	post2 := th.CreatePost()
   708  	post3 := &model.Post{ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a", RootId: post1.Id}
   709  	post3, _ = Client.CreatePost(post3)
   710  
   711  	time.Sleep(300 * time.Millisecond)
   712  	since := model.GetMillis()
   713  	time.Sleep(300 * time.Millisecond)
   714  
   715  	post4 := th.CreatePost()
   716  
   717  	posts, resp := Client.GetPostsForChannel(th.BasicChannel.Id, 0, 60, "")
   718  	CheckNoError(t, resp)
   719  
   720  	if posts.Order[0] != post4.Id {
   721  		t.Fatal("wrong order")
   722  	}
   723  
   724  	if posts.Order[1] != post3.Id {
   725  		t.Fatal("wrong order")
   726  	}
   727  
   728  	if posts.Order[2] != post2.Id {
   729  		t.Fatal("wrong order")
   730  	}
   731  
   732  	if posts.Order[3] != post1.Id {
   733  		t.Fatal("wrong order")
   734  	}
   735  
   736  	posts, resp = Client.GetPostsForChannel(th.BasicChannel.Id, 0, 3, resp.Etag)
   737  	CheckEtag(t, posts, resp)
   738  
   739  	posts, resp = Client.GetPostsForChannel(th.BasicChannel.Id, 0, 3, "")
   740  	CheckNoError(t, resp)
   741  
   742  	if len(posts.Order) != 3 {
   743  		t.Fatal("wrong number returned")
   744  	}
   745  
   746  	if _, ok := posts.Posts[post3.Id]; !ok {
   747  		t.Fatal("missing comment")
   748  	}
   749  
   750  	if _, ok := posts.Posts[post1.Id]; !ok {
   751  		t.Fatal("missing root post")
   752  	}
   753  
   754  	posts, resp = Client.GetPostsForChannel(th.BasicChannel.Id, 1, 1, "")
   755  	CheckNoError(t, resp)
   756  
   757  	if posts.Order[0] != post3.Id {
   758  		t.Fatal("wrong order")
   759  	}
   760  
   761  	posts, resp = Client.GetPostsForChannel(th.BasicChannel.Id, 10000, 10000, "")
   762  	CheckNoError(t, resp)
   763  
   764  	if len(posts.Order) != 0 {
   765  		t.Fatal("should be no posts")
   766  	}
   767  
   768  	post5 := th.CreatePost()
   769  
   770  	posts, resp = Client.GetPostsSince(th.BasicChannel.Id, since)
   771  	CheckNoError(t, resp)
   772  
   773  	if len(posts.Posts) != 2 {
   774  		t.Log(posts.Posts)
   775  		t.Fatal("should return 2 posts")
   776  	}
   777  
   778  	found := make([]bool, 2)
   779  	for _, p := range posts.Posts {
   780  		if p.CreateAt < since {
   781  			t.Fatal("bad create at for post returned")
   782  		}
   783  		if p.Id == post4.Id {
   784  			found[0] = true
   785  		} else if p.Id == post5.Id {
   786  			found[1] = true
   787  		}
   788  	}
   789  
   790  	for _, f := range found {
   791  		if !f {
   792  			t.Fatal("missing post")
   793  		}
   794  	}
   795  
   796  	_, resp = Client.GetPostsForChannel("", 0, 60, "")
   797  	CheckBadRequestStatus(t, resp)
   798  
   799  	_, resp = Client.GetPostsForChannel("junk", 0, 60, "")
   800  	CheckBadRequestStatus(t, resp)
   801  
   802  	_, resp = Client.GetPostsForChannel(model.NewId(), 0, 60, "")
   803  	CheckForbiddenStatus(t, resp)
   804  
   805  	Client.Logout()
   806  	_, resp = Client.GetPostsForChannel(model.NewId(), 0, 60, "")
   807  	CheckUnauthorizedStatus(t, resp)
   808  
   809  	_, resp = th.SystemAdminClient.GetPostsForChannel(th.BasicChannel.Id, 0, 60, "")
   810  	CheckNoError(t, resp)
   811  }
   812  
   813  func TestGetFlaggedPostsForUser(t *testing.T) {
   814  	th := Setup().InitBasic().InitSystemAdmin()
   815  	defer th.TearDown()
   816  	Client := th.Client
   817  	user := th.BasicUser
   818  	team1 := th.BasicTeam
   819  	channel1 := th.BasicChannel
   820  	post1 := th.CreatePost()
   821  	channel2 := th.CreatePublicChannel()
   822  	post2 := th.CreatePostWithClient(Client, channel2)
   823  
   824  	preference := model.Preference{
   825  		UserId:   user.Id,
   826  		Category: model.PREFERENCE_CATEGORY_FLAGGED_POST,
   827  		Name:     post1.Id,
   828  		Value:    "true",
   829  	}
   830  	Client.UpdatePreferences(user.Id, &model.Preferences{preference})
   831  	preference.Name = post2.Id
   832  	Client.UpdatePreferences(user.Id, &model.Preferences{preference})
   833  
   834  	opl := model.NewPostList()
   835  	opl.AddPost(post1)
   836  	opl.AddOrder(post1.Id)
   837  
   838  	rpl, resp := Client.GetFlaggedPostsForUserInChannel(user.Id, channel1.Id, 0, 10)
   839  	CheckNoError(t, resp)
   840  
   841  	if len(rpl.Posts) != 1 {
   842  		t.Fatal("should have returned 1 post")
   843  	}
   844  
   845  	if !reflect.DeepEqual(rpl.Posts, opl.Posts) {
   846  		t.Fatal("posts should have matched")
   847  	}
   848  
   849  	rpl, resp = Client.GetFlaggedPostsForUserInChannel(user.Id, channel1.Id, 0, 1)
   850  	CheckNoError(t, resp)
   851  
   852  	if len(rpl.Posts) != 1 {
   853  		t.Fatal("should have returned 1 post")
   854  	}
   855  
   856  	rpl, resp = Client.GetFlaggedPostsForUserInChannel(user.Id, channel1.Id, 1, 1)
   857  	CheckNoError(t, resp)
   858  
   859  	if len(rpl.Posts) != 0 {
   860  		t.Fatal("should be empty")
   861  	}
   862  
   863  	rpl, resp = Client.GetFlaggedPostsForUserInChannel(user.Id, GenerateTestId(), 0, 10)
   864  	CheckNoError(t, resp)
   865  
   866  	if len(rpl.Posts) != 0 {
   867  		t.Fatal("should be empty")
   868  	}
   869  
   870  	rpl, resp = Client.GetFlaggedPostsForUserInChannel(user.Id, "junk", 0, 10)
   871  	CheckBadRequestStatus(t, resp)
   872  
   873  	if rpl != nil {
   874  		t.Fatal("should be nil")
   875  	}
   876  
   877  	opl.AddPost(post2)
   878  	opl.AddOrder(post2.Id)
   879  
   880  	rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 0, 10)
   881  	CheckNoError(t, resp)
   882  
   883  	if len(rpl.Posts) != 2 {
   884  		t.Fatal("should have returned 2 posts")
   885  	}
   886  
   887  	if !reflect.DeepEqual(rpl.Posts, opl.Posts) {
   888  		t.Fatal("posts should have matched")
   889  	}
   890  
   891  	rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 0, 1)
   892  	CheckNoError(t, resp)
   893  
   894  	if len(rpl.Posts) != 1 {
   895  		t.Fatal("should have returned 1 post")
   896  	}
   897  
   898  	rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 1, 1)
   899  	CheckNoError(t, resp)
   900  
   901  	if len(rpl.Posts) != 1 {
   902  		t.Fatal("should have returned 1 post")
   903  	}
   904  
   905  	rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 1000, 10)
   906  	CheckNoError(t, resp)
   907  
   908  	if len(rpl.Posts) != 0 {
   909  		t.Fatal("should be empty")
   910  	}
   911  
   912  	rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, GenerateTestId(), 0, 10)
   913  	CheckNoError(t, resp)
   914  
   915  	if len(rpl.Posts) != 0 {
   916  		t.Fatal("should be empty")
   917  	}
   918  
   919  	rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, "junk", 0, 10)
   920  	CheckBadRequestStatus(t, resp)
   921  
   922  	if rpl != nil {
   923  		t.Fatal("should be nil")
   924  	}
   925  
   926  	channel3 := th.CreatePrivateChannel()
   927  	post4 := th.CreatePostWithClient(Client, channel3)
   928  
   929  	preference.Name = post4.Id
   930  	Client.UpdatePreferences(user.Id, &model.Preferences{preference})
   931  
   932  	opl.AddPost(post4)
   933  	opl.AddOrder(post4.Id)
   934  
   935  	rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 0, 10)
   936  	CheckNoError(t, resp)
   937  
   938  	if len(rpl.Posts) != 3 {
   939  		t.Fatal("should have returned 3 posts")
   940  	}
   941  
   942  	if !reflect.DeepEqual(rpl.Posts, opl.Posts) {
   943  		t.Fatal("posts should have matched")
   944  	}
   945  
   946  	rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 0, 2)
   947  	CheckNoError(t, resp)
   948  
   949  	if len(rpl.Posts) != 2 {
   950  		t.Fatal("should have returned 2 posts")
   951  	}
   952  
   953  	rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 2, 2)
   954  	CheckNoError(t, resp)
   955  
   956  	if len(rpl.Posts) != 1 {
   957  		t.Fatal("should have returned 1 post")
   958  	}
   959  
   960  	rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 1000, 10)
   961  	CheckNoError(t, resp)
   962  
   963  	if len(rpl.Posts) != 0 {
   964  		t.Fatal("should be empty")
   965  	}
   966  
   967  	_, resp = Client.GetFlaggedPostsForUser("junk", 0, 10)
   968  	CheckBadRequestStatus(t, resp)
   969  
   970  	_, resp = Client.GetFlaggedPostsForUser(GenerateTestId(), 0, 10)
   971  	CheckForbiddenStatus(t, resp)
   972  
   973  	Client.Logout()
   974  
   975  	rpl, resp = Client.GetFlaggedPostsForUserInChannel(user.Id, channel1.Id, 0, 10)
   976  	CheckUnauthorizedStatus(t, resp)
   977  
   978  	rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 0, 10)
   979  	CheckUnauthorizedStatus(t, resp)
   980  
   981  	rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 0, 10)
   982  	CheckUnauthorizedStatus(t, resp)
   983  
   984  	rpl, resp = th.SystemAdminClient.GetFlaggedPostsForUserInChannel(user.Id, channel1.Id, 0, 10)
   985  	CheckNoError(t, resp)
   986  
   987  	rpl, resp = th.SystemAdminClient.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 0, 10)
   988  	CheckNoError(t, resp)
   989  
   990  	rpl, resp = th.SystemAdminClient.GetFlaggedPostsForUser(user.Id, 0, 10)
   991  	CheckNoError(t, resp)
   992  }
   993  
   994  func TestGetPostsAfterAndBefore(t *testing.T) {
   995  	th := Setup().InitBasic()
   996  	defer th.TearDown()
   997  	Client := th.Client
   998  
   999  	post1 := th.CreatePost()
  1000  	post2 := th.CreatePost()
  1001  	post3 := th.CreatePost()
  1002  	post4 := th.CreatePost()
  1003  	post5 := th.CreatePost()
  1004  
  1005  	posts, resp := Client.GetPostsBefore(th.BasicChannel.Id, post3.Id, 0, 100, "")
  1006  	CheckNoError(t, resp)
  1007  
  1008  	found := make([]bool, 2)
  1009  	for _, p := range posts.Posts {
  1010  		if p.Id == post1.Id {
  1011  			found[0] = true
  1012  		} else if p.Id == post2.Id {
  1013  			found[1] = true
  1014  		}
  1015  
  1016  		if p.Id == post4.Id || p.Id == post5.Id {
  1017  			t.Fatal("returned posts after")
  1018  		}
  1019  	}
  1020  
  1021  	for _, f := range found {
  1022  		if !f {
  1023  			t.Fatal("missing post")
  1024  		}
  1025  	}
  1026  
  1027  	posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post3.Id, 1, 1, "")
  1028  	CheckNoError(t, resp)
  1029  
  1030  	if len(posts.Posts) != 1 {
  1031  		t.Fatal("too many posts returned")
  1032  	}
  1033  
  1034  	posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, "junk", 1, 1, "")
  1035  	CheckNoError(t, resp)
  1036  
  1037  	if len(posts.Posts) != 0 {
  1038  		t.Fatal("should have no posts")
  1039  	}
  1040  
  1041  	posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post3.Id, 0, 100, "")
  1042  	CheckNoError(t, resp)
  1043  
  1044  	found = make([]bool, 2)
  1045  	for _, p := range posts.Posts {
  1046  		if p.Id == post4.Id {
  1047  			found[0] = true
  1048  		} else if p.Id == post5.Id {
  1049  			found[1] = true
  1050  		}
  1051  
  1052  		if p.Id == post1.Id || p.Id == post2.Id {
  1053  			t.Fatal("returned posts before")
  1054  		}
  1055  	}
  1056  
  1057  	for _, f := range found {
  1058  		if !f {
  1059  			t.Fatal("missing post")
  1060  		}
  1061  	}
  1062  
  1063  	posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post3.Id, 1, 1, "")
  1064  	CheckNoError(t, resp)
  1065  
  1066  	if len(posts.Posts) != 1 {
  1067  		t.Fatal("too many posts returned")
  1068  	}
  1069  
  1070  	posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, "junk", 1, 1, "")
  1071  	CheckNoError(t, resp)
  1072  
  1073  	if len(posts.Posts) != 0 {
  1074  		t.Fatal("should have no posts")
  1075  	}
  1076  }
  1077  
  1078  func TestGetPost(t *testing.T) {
  1079  	th := Setup().InitBasic().InitSystemAdmin()
  1080  	defer th.TearDown()
  1081  	Client := th.Client
  1082  
  1083  	post, resp := Client.GetPost(th.BasicPost.Id, "")
  1084  	CheckNoError(t, resp)
  1085  
  1086  	if post.Id != th.BasicPost.Id {
  1087  		t.Fatal("post ids don't match")
  1088  	}
  1089  
  1090  	post, resp = Client.GetPost(th.BasicPost.Id, resp.Etag)
  1091  	CheckEtag(t, post, resp)
  1092  
  1093  	_, resp = Client.GetPost("", "")
  1094  	CheckNotFoundStatus(t, resp)
  1095  
  1096  	_, resp = Client.GetPost("junk", "")
  1097  	CheckBadRequestStatus(t, resp)
  1098  
  1099  	_, resp = Client.GetPost(model.NewId(), "")
  1100  	CheckNotFoundStatus(t, resp)
  1101  
  1102  	Client.RemoveUserFromChannel(th.BasicChannel.Id, th.BasicUser.Id)
  1103  
  1104  	// Channel is public, should be able to read post
  1105  	post, resp = Client.GetPost(th.BasicPost.Id, "")
  1106  	CheckNoError(t, resp)
  1107  
  1108  	privatePost := th.CreatePostWithClient(Client, th.BasicPrivateChannel)
  1109  
  1110  	post, resp = Client.GetPost(privatePost.Id, "")
  1111  	CheckNoError(t, resp)
  1112  
  1113  	Client.RemoveUserFromChannel(th.BasicPrivateChannel.Id, th.BasicUser.Id)
  1114  
  1115  	// Channel is private, should not be able to read post
  1116  	post, resp = Client.GetPost(privatePost.Id, "")
  1117  	CheckForbiddenStatus(t, resp)
  1118  
  1119  	Client.Logout()
  1120  	_, resp = Client.GetPost(model.NewId(), "")
  1121  	CheckUnauthorizedStatus(t, resp)
  1122  
  1123  	post, resp = th.SystemAdminClient.GetPost(th.BasicPost.Id, "")
  1124  	CheckNoError(t, resp)
  1125  }
  1126  
  1127  func TestDeletePost(t *testing.T) {
  1128  	th := Setup().InitBasic().InitSystemAdmin()
  1129  	defer th.TearDown()
  1130  	Client := th.Client
  1131  
  1132  	_, resp := Client.DeletePost("")
  1133  	CheckNotFoundStatus(t, resp)
  1134  
  1135  	_, resp = Client.DeletePost("junk")
  1136  	CheckBadRequestStatus(t, resp)
  1137  
  1138  	_, resp = Client.DeletePost(th.BasicPost.Id)
  1139  	CheckForbiddenStatus(t, resp)
  1140  
  1141  	Client.Login(th.TeamAdminUser.Email, th.TeamAdminUser.Password)
  1142  	_, resp = Client.DeletePost(th.BasicPost.Id)
  1143  	CheckNoError(t, resp)
  1144  
  1145  	post := th.CreatePost()
  1146  	user := th.CreateUser()
  1147  
  1148  	Client.Logout()
  1149  	Client.Login(user.Email, user.Password)
  1150  
  1151  	_, resp = Client.DeletePost(post.Id)
  1152  	CheckForbiddenStatus(t, resp)
  1153  
  1154  	Client.Logout()
  1155  	_, resp = Client.DeletePost(model.NewId())
  1156  	CheckUnauthorizedStatus(t, resp)
  1157  
  1158  	status, resp := th.SystemAdminClient.DeletePost(post.Id)
  1159  	if !status {
  1160  		t.Fatal("post should return status OK")
  1161  	}
  1162  	CheckNoError(t, resp)
  1163  }
  1164  
  1165  func TestGetPostThread(t *testing.T) {
  1166  	th := Setup().InitBasic().InitSystemAdmin()
  1167  	defer th.TearDown()
  1168  	Client := th.Client
  1169  
  1170  	post := &model.Post{ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a", RootId: th.BasicPost.Id}
  1171  	post, _ = Client.CreatePost(post)
  1172  
  1173  	list, resp := Client.GetPostThread(th.BasicPost.Id, "")
  1174  	CheckNoError(t, resp)
  1175  
  1176  	var list2 *model.PostList
  1177  	list2, resp = Client.GetPostThread(th.BasicPost.Id, resp.Etag)
  1178  	CheckEtag(t, list2, resp)
  1179  
  1180  	if list.Order[0] != th.BasicPost.Id {
  1181  		t.Fatal("wrong order")
  1182  	}
  1183  
  1184  	if _, ok := list.Posts[th.BasicPost.Id]; !ok {
  1185  		t.Fatal("should have had post")
  1186  	}
  1187  
  1188  	if _, ok := list.Posts[post.Id]; !ok {
  1189  		t.Fatal("should have had post")
  1190  	}
  1191  
  1192  	_, resp = Client.GetPostThread("junk", "")
  1193  	CheckBadRequestStatus(t, resp)
  1194  
  1195  	_, resp = Client.GetPostThread(model.NewId(), "")
  1196  	CheckNotFoundStatus(t, resp)
  1197  
  1198  	Client.RemoveUserFromChannel(th.BasicChannel.Id, th.BasicUser.Id)
  1199  
  1200  	// Channel is public, should be able to read post
  1201  	_, resp = Client.GetPostThread(th.BasicPost.Id, "")
  1202  	CheckNoError(t, resp)
  1203  
  1204  	privatePost := th.CreatePostWithClient(Client, th.BasicPrivateChannel)
  1205  
  1206  	_, resp = Client.GetPostThread(privatePost.Id, "")
  1207  	CheckNoError(t, resp)
  1208  
  1209  	Client.RemoveUserFromChannel(th.BasicPrivateChannel.Id, th.BasicUser.Id)
  1210  
  1211  	// Channel is private, should not be able to read post
  1212  	_, resp = Client.GetPostThread(privatePost.Id, "")
  1213  	CheckForbiddenStatus(t, resp)
  1214  
  1215  	Client.Logout()
  1216  	_, resp = Client.GetPostThread(model.NewId(), "")
  1217  	CheckUnauthorizedStatus(t, resp)
  1218  
  1219  	list, resp = th.SystemAdminClient.GetPostThread(th.BasicPost.Id, "")
  1220  	CheckNoError(t, resp)
  1221  }
  1222  
  1223  func TestSearchPosts(t *testing.T) {
  1224  	th := Setup().InitBasic()
  1225  	defer th.TearDown()
  1226  	th.LoginBasic()
  1227  	Client := th.Client
  1228  
  1229  	message := "search for post1"
  1230  	_ = th.CreateMessagePost(message)
  1231  
  1232  	message = "search for post2"
  1233  	post2 := th.CreateMessagePost(message)
  1234  
  1235  	message = "#hashtag search for post3"
  1236  	post3 := th.CreateMessagePost(message)
  1237  
  1238  	message = "hashtag for post4"
  1239  	_ = th.CreateMessagePost(message)
  1240  
  1241  	posts, resp := Client.SearchPosts(th.BasicTeam.Id, "search", false)
  1242  	CheckNoError(t, resp)
  1243  	if len(posts.Order) != 3 {
  1244  		t.Fatal("wrong search")
  1245  	}
  1246  
  1247  	posts, resp = Client.SearchPosts(th.BasicTeam.Id, "post2", false)
  1248  	CheckNoError(t, resp)
  1249  	if len(posts.Order) != 1 && posts.Order[0] == post2.Id {
  1250  		t.Fatal("wrong search")
  1251  	}
  1252  
  1253  	posts, resp = Client.SearchPosts(th.BasicTeam.Id, "#hashtag", false)
  1254  	CheckNoError(t, resp)
  1255  	if len(posts.Order) != 1 && posts.Order[0] == post3.Id {
  1256  		t.Fatal("wrong search")
  1257  	}
  1258  
  1259  	if posts, resp = Client.SearchPosts(th.BasicTeam.Id, "*", false); len(posts.Order) != 0 {
  1260  		t.Fatal("searching for just * shouldn't return any results")
  1261  	}
  1262  
  1263  	posts, resp = Client.SearchPosts(th.BasicTeam.Id, "post1 post2", true)
  1264  	CheckNoError(t, resp)
  1265  	if len(posts.Order) != 2 {
  1266  		t.Fatal("wrong search results")
  1267  	}
  1268  
  1269  	_, resp = Client.SearchPosts("junk", "#sgtitlereview", false)
  1270  	CheckBadRequestStatus(t, resp)
  1271  
  1272  	_, resp = Client.SearchPosts(model.NewId(), "#sgtitlereview", false)
  1273  	CheckForbiddenStatus(t, resp)
  1274  
  1275  	_, resp = Client.SearchPosts(th.BasicTeam.Id, "", false)
  1276  	CheckBadRequestStatus(t, resp)
  1277  
  1278  	Client.Logout()
  1279  	_, resp = Client.SearchPosts(th.BasicTeam.Id, "#sgtitlereview", false)
  1280  	CheckUnauthorizedStatus(t, resp)
  1281  
  1282  }
  1283  
  1284  func TestSearchHashtagPosts(t *testing.T) {
  1285  	th := Setup().InitBasic()
  1286  	defer th.TearDown()
  1287  	th.LoginBasic()
  1288  	Client := th.Client
  1289  
  1290  	message := "#sgtitlereview with space"
  1291  	_ = th.CreateMessagePost(message)
  1292  
  1293  	message = "#sgtitlereview\n with return"
  1294  	_ = th.CreateMessagePost(message)
  1295  
  1296  	message = "no hashtag"
  1297  	_ = th.CreateMessagePost(message)
  1298  
  1299  	posts, resp := Client.SearchPosts(th.BasicTeam.Id, "#sgtitlereview", false)
  1300  	CheckNoError(t, resp)
  1301  	if len(posts.Order) != 2 {
  1302  		t.Fatal("wrong search results")
  1303  	}
  1304  
  1305  	Client.Logout()
  1306  	_, resp = Client.SearchPosts(th.BasicTeam.Id, "#sgtitlereview", false)
  1307  	CheckUnauthorizedStatus(t, resp)
  1308  }
  1309  
  1310  func TestSearchPostsInChannel(t *testing.T) {
  1311  	th := Setup().InitBasic()
  1312  	defer th.TearDown()
  1313  	th.LoginBasic()
  1314  	Client := th.Client
  1315  
  1316  	channel := th.CreatePublicChannel()
  1317  
  1318  	message := "sgtitlereview with space"
  1319  	_ = th.CreateMessagePost(message)
  1320  
  1321  	message = "sgtitlereview\n with return"
  1322  	_ = th.CreateMessagePostWithClient(Client, th.BasicChannel2, message)
  1323  
  1324  	message = "other message with no return"
  1325  	_ = th.CreateMessagePostWithClient(Client, th.BasicChannel2, message)
  1326  
  1327  	message = "other message with no return"
  1328  	_ = th.CreateMessagePostWithClient(Client, channel, message)
  1329  
  1330  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "channel:", false); len(posts.Order) != 0 {
  1331  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1332  	}
  1333  
  1334  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "in:", false); len(posts.Order) != 0 {
  1335  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1336  	}
  1337  
  1338  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "channel:"+th.BasicChannel.Name, false); len(posts.Order) != 2 {
  1339  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1340  	}
  1341  
  1342  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "in:"+th.BasicChannel2.Name, false); len(posts.Order) != 2 {
  1343  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1344  	}
  1345  
  1346  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "channel:"+th.BasicChannel2.Name, false); len(posts.Order) != 2 {
  1347  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1348  	}
  1349  
  1350  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "ChAnNeL:"+th.BasicChannel2.Name, false); len(posts.Order) != 2 {
  1351  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1352  	}
  1353  
  1354  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "sgtitlereview", false); len(posts.Order) != 2 {
  1355  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1356  	}
  1357  
  1358  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "sgtitlereview channel:"+th.BasicChannel.Name, false); len(posts.Order) != 1 {
  1359  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1360  	}
  1361  
  1362  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "sgtitlereview in: "+th.BasicChannel2.Name, false); len(posts.Order) != 1 {
  1363  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1364  	}
  1365  
  1366  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "sgtitlereview channel: "+th.BasicChannel2.Name, false); len(posts.Order) != 1 {
  1367  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1368  	}
  1369  
  1370  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "channel: "+th.BasicChannel2.Name+" channel: "+channel.Name, false); len(posts.Order) != 3 {
  1371  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1372  	}
  1373  
  1374  }
  1375  
  1376  func TestSearchPostsFromUser(t *testing.T) {
  1377  	th := Setup().InitBasic()
  1378  	defer th.TearDown()
  1379  	Client := th.Client
  1380  
  1381  	th.LoginTeamAdmin()
  1382  	user := th.CreateUser()
  1383  	th.LinkUserToTeam(user, th.BasicTeam)
  1384  	th.App.AddUserToChannel(user, th.BasicChannel)
  1385  	th.App.AddUserToChannel(user, th.BasicChannel2)
  1386  
  1387  	message := "sgtitlereview with space"
  1388  	_ = th.CreateMessagePost(message)
  1389  
  1390  	Client.Logout()
  1391  	th.LoginBasic2()
  1392  
  1393  	message = "sgtitlereview\n with return"
  1394  	_ = th.CreateMessagePostWithClient(Client, th.BasicChannel2, message)
  1395  
  1396  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.TeamAdminUser.Username, false); len(posts.Order) != 2 {
  1397  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1398  	}
  1399  
  1400  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username, false); len(posts.Order) != 1 {
  1401  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1402  	}
  1403  
  1404  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username+" sgtitlereview", false); len(posts.Order) != 1 {
  1405  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1406  	}
  1407  
  1408  	message = "hullo"
  1409  	_ = th.CreateMessagePost(message)
  1410  
  1411  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username+" in:"+th.BasicChannel.Name, false); len(posts.Order) != 1 {
  1412  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1413  	}
  1414  
  1415  	Client.Login(user.Email, user.Password)
  1416  
  1417  	// wait for the join/leave messages to be created for user3 since they're done asynchronously
  1418  	time.Sleep(100 * time.Millisecond)
  1419  
  1420  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username, false); len(posts.Order) != 2 {
  1421  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1422  	}
  1423  
  1424  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username+" from: "+user.Username, false); len(posts.Order) != 2 {
  1425  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1426  	}
  1427  
  1428  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username+" from: "+user.Username+" in:"+th.BasicChannel2.Name, false); len(posts.Order) != 1 {
  1429  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1430  	}
  1431  
  1432  	message = "coconut"
  1433  	_ = th.CreateMessagePostWithClient(Client, th.BasicChannel2, message)
  1434  
  1435  	if posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username+" from: "+user.Username+" in:"+th.BasicChannel2.Name+" coconut", false); len(posts.Order) != 1 {
  1436  		t.Fatalf("wrong number of posts returned %v", len(posts.Order))
  1437  	}
  1438  }
  1439  
  1440  func TestGetFileInfosForPost(t *testing.T) {
  1441  	th := Setup().InitBasic().InitSystemAdmin()
  1442  	defer th.TearDown()
  1443  	Client := th.Client
  1444  
  1445  	fileIds := make([]string, 3)
  1446  	if data, err := readTestFile("test.png"); err != nil {
  1447  		t.Fatal(err)
  1448  	} else {
  1449  		for i := 0; i < 3; i++ {
  1450  			fileResp, _ := Client.UploadFile(data, th.BasicChannel.Id, "test.png")
  1451  			fileIds[i] = fileResp.FileInfos[0].Id
  1452  		}
  1453  	}
  1454  
  1455  	post := &model.Post{ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a", FileIds: fileIds}
  1456  	post, _ = Client.CreatePost(post)
  1457  
  1458  	infos, resp := Client.GetFileInfosForPost(post.Id, "")
  1459  	CheckNoError(t, resp)
  1460  
  1461  	if len(infos) != 3 {
  1462  		t.Fatal("missing file infos")
  1463  	}
  1464  
  1465  	found := false
  1466  	for _, info := range infos {
  1467  		if info.Id == fileIds[0] {
  1468  			found = true
  1469  		}
  1470  	}
  1471  
  1472  	if !found {
  1473  		t.Fatal("missing file info")
  1474  	}
  1475  
  1476  	infos, resp = Client.GetFileInfosForPost(post.Id, resp.Etag)
  1477  	CheckEtag(t, infos, resp)
  1478  
  1479  	infos, resp = Client.GetFileInfosForPost(th.BasicPost.Id, "")
  1480  	CheckNoError(t, resp)
  1481  
  1482  	if len(infos) != 0 {
  1483  		t.Fatal("should have no file infos")
  1484  	}
  1485  
  1486  	_, resp = Client.GetFileInfosForPost("junk", "")
  1487  	CheckBadRequestStatus(t, resp)
  1488  
  1489  	_, resp = Client.GetFileInfosForPost(model.NewId(), "")
  1490  	CheckForbiddenStatus(t, resp)
  1491  
  1492  	Client.Logout()
  1493  	_, resp = Client.GetFileInfosForPost(model.NewId(), "")
  1494  	CheckUnauthorizedStatus(t, resp)
  1495  
  1496  	_, resp = th.SystemAdminClient.GetFileInfosForPost(th.BasicPost.Id, "")
  1497  	CheckNoError(t, resp)
  1498  }