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