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