github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/api4/post_test.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package api4 5 6 import ( 7 "context" 8 "encoding/json" 9 "errors" 10 "fmt" 11 "net/http" 12 "net/http/httptest" 13 "net/url" 14 "os" 15 "reflect" 16 "sort" 17 "strings" 18 "testing" 19 "time" 20 21 "github.com/stretchr/testify/assert" 22 "github.com/stretchr/testify/require" 23 24 "github.com/masterhung0112/hk_server/v5/app" 25 "github.com/masterhung0112/hk_server/v5/model" 26 "github.com/masterhung0112/hk_server/v5/plugin/plugintest/mock" 27 "github.com/masterhung0112/hk_server/v5/store/storetest/mocks" 28 "github.com/masterhung0112/hk_server/v5/utils" 29 "github.com/masterhung0112/hk_server/v5/utils/testutils" 30 ) 31 32 func TestCreatePost(t *testing.T) { 33 th := Setup(t).InitBasic() 34 defer th.TearDown() 35 Client := th.Client 36 37 post := &model.Post{ChannelId: th.BasicChannel.Id, Message: "#hashtag a" + model.NewId() + "a", Props: model.StringInterface{model.PROPS_ADD_CHANNEL_MEMBER: "no good"}} 38 rpost, resp := Client.CreatePost(post) 39 CheckNoError(t, resp) 40 CheckCreatedStatus(t, resp) 41 42 require.Equal(t, post.Message, rpost.Message, "message didn't match") 43 require.Equal(t, "#hashtag", rpost.Hashtags, "hashtag didn't match") 44 require.Empty(t, rpost.FileIds) 45 require.Equal(t, 0, int(rpost.EditAt), "newly created post shouldn't have EditAt set") 46 require.Nil(t, rpost.GetProp(model.PROPS_ADD_CHANNEL_MEMBER), "newly created post shouldn't have Props['add_channel_member'] set") 47 48 post.RootId = rpost.Id 49 post.ParentId = rpost.Id 50 _, resp = Client.CreatePost(post) 51 CheckNoError(t, resp) 52 53 post.RootId = "junk" 54 _, resp = Client.CreatePost(post) 55 CheckBadRequestStatus(t, resp) 56 57 post.RootId = rpost.Id 58 post.ParentId = "junk" 59 _, resp = Client.CreatePost(post) 60 CheckBadRequestStatus(t, resp) 61 62 post2 := &model.Post{ChannelId: th.BasicChannel2.Id, Message: "zz" + model.NewId() + "a", CreateAt: 123} 63 rpost2, _ := Client.CreatePost(post2) 64 require.NotEqual(t, post2.CreateAt, rpost2.CreateAt, "create at should not match") 65 66 t.Run("with file uploaded by same user", func(t *testing.T) { 67 fileResp, subResponse := Client.UploadFile([]byte("data"), th.BasicChannel.Id, "test") 68 CheckNoError(t, subResponse) 69 fileId := fileResp.FileInfos[0].Id 70 71 postWithFiles, subResponse := Client.CreatePost(&model.Post{ 72 ChannelId: th.BasicChannel.Id, 73 Message: "with files", 74 FileIds: model.StringArray{fileId}, 75 }) 76 CheckNoError(t, subResponse) 77 assert.Equal(t, model.StringArray{fileId}, postWithFiles.FileIds) 78 79 actualPostWithFiles, subResponse := Client.GetPost(postWithFiles.Id, "") 80 CheckNoError(t, subResponse) 81 assert.Equal(t, model.StringArray{fileId}, actualPostWithFiles.FileIds) 82 }) 83 84 t.Run("with file uploaded by different user", func(t *testing.T) { 85 fileResp, subResponse := th.SystemAdminClient.UploadFile([]byte("data"), th.BasicChannel.Id, "test") 86 CheckNoError(t, subResponse) 87 fileId := fileResp.FileInfos[0].Id 88 89 postWithFiles, subResponse := Client.CreatePost(&model.Post{ 90 ChannelId: th.BasicChannel.Id, 91 Message: "with files", 92 FileIds: model.StringArray{fileId}, 93 }) 94 CheckNoError(t, subResponse) 95 assert.Empty(t, postWithFiles.FileIds) 96 97 actualPostWithFiles, subResponse := Client.GetPost(postWithFiles.Id, "") 98 CheckNoError(t, subResponse) 99 assert.Empty(t, actualPostWithFiles.FileIds) 100 }) 101 102 t.Run("with file uploaded by nouser", func(t *testing.T) { 103 fileInfo, err := th.App.UploadFile(th.Context, []byte("data"), th.BasicChannel.Id, "test") 104 require.Nil(t, err) 105 fileId := fileInfo.Id 106 107 postWithFiles, subResponse := Client.CreatePost(&model.Post{ 108 ChannelId: th.BasicChannel.Id, 109 Message: "with files", 110 FileIds: model.StringArray{fileId}, 111 }) 112 CheckNoError(t, subResponse) 113 assert.Equal(t, model.StringArray{fileId}, postWithFiles.FileIds) 114 115 actualPostWithFiles, subResponse := Client.GetPost(postWithFiles.Id, "") 116 CheckNoError(t, subResponse) 117 assert.Equal(t, model.StringArray{fileId}, actualPostWithFiles.FileIds) 118 }) 119 120 t.Run("Create posts without the USE_CHANNEL_MENTIONS Permission - returns ephemeral message with mentions and no ephemeral message without mentions", func(t *testing.T) { 121 WebSocketClient, err := th.CreateWebSocketClient() 122 WebSocketClient.Listen() 123 require.Nil(t, err) 124 125 defer th.RestoreDefaultRolePermissions(th.SaveDefaultRolePermissions()) 126 127 th.RemovePermissionFromRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_USER_ROLE_ID) 128 129 post.RootId = rpost.Id 130 post.ParentId = rpost.Id 131 post.Message = "a post with no channel mentions" 132 _, resp = Client.CreatePost(post) 133 CheckNoError(t, resp) 134 135 // Message with no channel mentions should result in no ephemeral message 136 timeout := time.After(300 * time.Millisecond) 137 waiting := true 138 for waiting { 139 select { 140 case event := <-WebSocketClient.EventChannel: 141 require.NotEqual(t, model.WEBSOCKET_EVENT_EPHEMERAL_MESSAGE, event.EventType(), "should not have ephemeral message event") 142 case <-timeout: 143 waiting = false 144 } 145 } 146 147 post.RootId = rpost.Id 148 post.ParentId = rpost.Id 149 post.Message = "a post with @channel" 150 _, resp = Client.CreatePost(post) 151 CheckNoError(t, resp) 152 153 post.RootId = rpost.Id 154 post.ParentId = rpost.Id 155 post.Message = "a post with @all" 156 _, resp = Client.CreatePost(post) 157 CheckNoError(t, resp) 158 159 post.RootId = rpost.Id 160 post.ParentId = rpost.Id 161 post.Message = "a post with @here" 162 _, resp = Client.CreatePost(post) 163 CheckNoError(t, resp) 164 165 timeout = time.After(600 * time.Millisecond) 166 eventsToGo := 3 // 3 Posts created with @ mentions should result in 3 websocket events 167 for eventsToGo > 0 { 168 select { 169 case event := <-WebSocketClient.EventChannel: 170 if event.Event == model.WEBSOCKET_EVENT_EPHEMERAL_MESSAGE { 171 require.Equal(t, model.WEBSOCKET_EVENT_EPHEMERAL_MESSAGE, event.Event) 172 eventsToGo = eventsToGo - 1 173 } 174 case <-timeout: 175 require.Fail(t, "Should have received ephemeral message event and not timedout") 176 eventsToGo = 0 177 } 178 } 179 }) 180 181 post.RootId = "" 182 post.ParentId = "" 183 post.Type = model.POST_SYSTEM_GENERIC 184 _, resp = Client.CreatePost(post) 185 CheckBadRequestStatus(t, resp) 186 187 post.Type = "" 188 post.RootId = rpost2.Id 189 post.ParentId = rpost2.Id 190 _, resp = Client.CreatePost(post) 191 CheckBadRequestStatus(t, resp) 192 193 post.RootId = "" 194 post.ParentId = "" 195 post.ChannelId = "junk" 196 _, resp = Client.CreatePost(post) 197 CheckForbiddenStatus(t, resp) 198 199 post.ChannelId = model.NewId() 200 _, resp = Client.CreatePost(post) 201 CheckForbiddenStatus(t, resp) 202 203 r, err := Client.DoApiPost("/posts", "garbage") 204 require.NotNil(t, err) 205 require.Equal(t, http.StatusBadRequest, r.StatusCode) 206 207 Client.Logout() 208 _, resp = Client.CreatePost(post) 209 CheckUnauthorizedStatus(t, resp) 210 211 post.ChannelId = th.BasicChannel.Id 212 post.CreateAt = 123 213 rpost, resp = th.SystemAdminClient.CreatePost(post) 214 CheckNoError(t, resp) 215 require.Equal(t, post.CreateAt, rpost.CreateAt, "create at should match") 216 } 217 218 func TestCreatePostEphemeral(t *testing.T) { 219 th := Setup(t).InitBasic() 220 defer th.TearDown() 221 Client := th.SystemAdminClient 222 223 ephemeralPost := &model.PostEphemeral{ 224 UserID: th.BasicUser2.Id, 225 Post: &model.Post{ChannelId: th.BasicChannel.Id, Message: "a" + model.NewId() + "a", Props: model.StringInterface{model.PROPS_ADD_CHANNEL_MEMBER: "no good"}}, 226 } 227 228 rpost, resp := Client.CreatePostEphemeral(ephemeralPost) 229 CheckNoError(t, resp) 230 CheckCreatedStatus(t, resp) 231 require.Equal(t, ephemeralPost.Post.Message, rpost.Message, "message didn't match") 232 require.Equal(t, 0, int(rpost.EditAt), "newly created ephemeral post shouldn't have EditAt set") 233 234 r, err := Client.DoApiPost("/posts/ephemeral", "garbage") 235 require.NotNil(t, err) 236 require.Equal(t, http.StatusBadRequest, r.StatusCode) 237 238 Client.Logout() 239 _, resp = Client.CreatePostEphemeral(ephemeralPost) 240 CheckUnauthorizedStatus(t, resp) 241 242 Client = th.Client 243 _, resp = Client.CreatePostEphemeral(ephemeralPost) 244 CheckForbiddenStatus(t, resp) 245 } 246 247 func testCreatePostWithOutgoingHook( 248 t *testing.T, 249 hookContentType, expectedContentType, message, triggerWord string, 250 fileIds []string, 251 triggerWhen int, 252 commentPostType bool, 253 ) { 254 th := Setup(t).InitBasic() 255 defer th.TearDown() 256 user := th.SystemAdminUser 257 team := th.BasicTeam 258 channel := th.BasicChannel 259 260 enableOutgoingWebhooks := *th.App.Config().ServiceSettings.EnableOutgoingWebhooks 261 allowedUntrustedInternalConnections := *th.App.Config().ServiceSettings.AllowedUntrustedInternalConnections 262 defer func() { 263 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingWebhooks }) 264 th.App.UpdateConfig(func(cfg *model.Config) { 265 *cfg.ServiceSettings.AllowedUntrustedInternalConnections = allowedUntrustedInternalConnections 266 }) 267 }() 268 269 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOutgoingWebhooks = true }) 270 th.App.UpdateConfig(func(cfg *model.Config) { 271 *cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost,127.0.0.1" 272 }) 273 274 var hook *model.OutgoingWebhook 275 var post *model.Post 276 277 // Create a test server that is the target of the outgoing webhook. It will 278 // validate the webhook body fields and write to the success channel on 279 // success/failure. 280 success := make(chan bool) 281 wait := make(chan bool, 1) 282 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 283 <-wait 284 285 requestContentType := r.Header.Get("Content-Type") 286 if requestContentType != expectedContentType { 287 t.Logf("Content-Type is %s, should be %s", requestContentType, expectedContentType) 288 success <- false 289 return 290 } 291 292 expectedPayload := &model.OutgoingWebhookPayload{ 293 Token: hook.Token, 294 TeamId: hook.TeamId, 295 TeamDomain: team.Name, 296 ChannelId: post.ChannelId, 297 ChannelName: channel.Name, 298 Timestamp: post.CreateAt, 299 UserId: post.UserId, 300 UserName: user.Username, 301 PostId: post.Id, 302 Text: post.Message, 303 TriggerWord: triggerWord, 304 FileIds: strings.Join(post.FileIds, ","), 305 } 306 307 // depending on the Content-Type, we expect to find a JSON or form encoded payload 308 if requestContentType == "application/json" { 309 decoder := json.NewDecoder(r.Body) 310 o := &model.OutgoingWebhookPayload{} 311 decoder.Decode(&o) 312 313 if !reflect.DeepEqual(expectedPayload, o) { 314 t.Logf("JSON payload is %+v, should be %+v", o, expectedPayload) 315 success <- false 316 return 317 } 318 } else { 319 err := r.ParseForm() 320 if err != nil { 321 t.Logf("Error parsing form: %q", err) 322 success <- false 323 return 324 } 325 326 expectedFormValues, _ := url.ParseQuery(expectedPayload.ToFormValues()) 327 328 if !reflect.DeepEqual(expectedFormValues, r.Form) { 329 t.Logf("Form values are: %q\n, should be: %q\n", r.Form, expectedFormValues) 330 success <- false 331 return 332 } 333 } 334 335 respPostType := "" //if is empty or post will do a normal post. 336 if commentPostType { 337 respPostType = model.OUTGOING_HOOK_RESPONSE_TYPE_COMMENT 338 } 339 340 outGoingHookResponse := &model.OutgoingWebhookResponse{ 341 Text: model.NewString("some test text"), 342 Username: "TestCommandServer", 343 IconURL: "https://www.mattermost.org/wp-content/uploads/2016/04/icon.png", 344 Type: "custom_as", 345 ResponseType: respPostType, 346 } 347 348 fmt.Fprintf(w, outGoingHookResponse.ToJson()) 349 success <- true 350 })) 351 defer ts.Close() 352 353 // create an outgoing webhook, passing it the test server URL 354 var triggerWords []string 355 if triggerWord != "" { 356 triggerWords = []string{triggerWord} 357 } 358 359 hook = &model.OutgoingWebhook{ 360 ChannelId: channel.Id, 361 TeamId: team.Id, 362 ContentType: hookContentType, 363 TriggerWords: triggerWords, 364 TriggerWhen: triggerWhen, 365 CallbackURLs: []string{ts.URL}, 366 } 367 368 hook, resp := th.SystemAdminClient.CreateOutgoingWebhook(hook) 369 CheckNoError(t, resp) 370 371 // create a post to trigger the webhook 372 post = &model.Post{ 373 ChannelId: channel.Id, 374 Message: message, 375 FileIds: fileIds, 376 } 377 378 post, resp = th.SystemAdminClient.CreatePost(post) 379 CheckNoError(t, resp) 380 381 wait <- true 382 383 // We wait for the test server to write to the success channel and we make 384 // the test fail if that doesn't happen before the timeout. 385 select { 386 case ok := <-success: 387 require.True(t, ok, "Test server did send an invalid webhook.") 388 case <-time.After(time.Second): 389 require.FailNow(t, "Timeout, test server did not send the webhook.") 390 } 391 392 if commentPostType { 393 time.Sleep(time.Millisecond * 100) 394 postList, resp := th.SystemAdminClient.GetPostThread(post.Id, "", false) 395 CheckNoError(t, resp) 396 require.Equal(t, post.Id, postList.Order[0], "wrong order") 397 398 _, ok := postList.Posts[post.Id] 399 require.True(t, ok, "should have had post") 400 require.Len(t, postList.Posts, 2, "should have 2 posts") 401 } 402 } 403 404 func TestCreatePostWithOutgoingHook_form_urlencoded(t *testing.T) { 405 testCreatePostWithOutgoingHook(t, "application/x-www-form-urlencoded", "application/x-www-form-urlencoded", "triggerword lorem ipsum", "triggerword", []string{"file_id_1"}, app.TriggerwordsExactMatch, false) 406 testCreatePostWithOutgoingHook(t, "application/x-www-form-urlencoded", "application/x-www-form-urlencoded", "triggerwordaaazzz lorem ipsum", "triggerword", []string{"file_id_1"}, app.TriggerwordsStartsWith, false) 407 testCreatePostWithOutgoingHook(t, "application/x-www-form-urlencoded", "application/x-www-form-urlencoded", "", "", []string{"file_id_1"}, app.TriggerwordsExactMatch, false) 408 testCreatePostWithOutgoingHook(t, "application/x-www-form-urlencoded", "application/x-www-form-urlencoded", "", "", []string{"file_id_1"}, app.TriggerwordsStartsWith, false) 409 testCreatePostWithOutgoingHook(t, "application/x-www-form-urlencoded", "application/x-www-form-urlencoded", "triggerword lorem ipsum", "triggerword", []string{"file_id_1"}, app.TriggerwordsExactMatch, true) 410 testCreatePostWithOutgoingHook(t, "application/x-www-form-urlencoded", "application/x-www-form-urlencoded", "triggerwordaaazzz lorem ipsum", "triggerword", []string{"file_id_1"}, app.TriggerwordsStartsWith, true) 411 } 412 413 func TestCreatePostWithOutgoingHook_json(t *testing.T) { 414 testCreatePostWithOutgoingHook(t, "application/json", "application/json", "triggerword lorem ipsum", "triggerword", []string{"file_id_1, file_id_2"}, app.TriggerwordsExactMatch, false) 415 testCreatePostWithOutgoingHook(t, "application/json", "application/json", "triggerwordaaazzz lorem ipsum", "triggerword", []string{"file_id_1, file_id_2"}, app.TriggerwordsStartsWith, false) 416 testCreatePostWithOutgoingHook(t, "application/json", "application/json", "triggerword lorem ipsum", "", []string{"file_id_1"}, app.TriggerwordsExactMatch, false) 417 testCreatePostWithOutgoingHook(t, "application/json", "application/json", "triggerwordaaazzz lorem ipsum", "", []string{"file_id_1"}, app.TriggerwordsStartsWith, false) 418 testCreatePostWithOutgoingHook(t, "application/json", "application/json", "triggerword lorem ipsum", "triggerword", []string{"file_id_1, file_id_2"}, app.TriggerwordsExactMatch, true) 419 testCreatePostWithOutgoingHook(t, "application/json", "application/json", "triggerwordaaazzz lorem ipsum", "", []string{"file_id_1"}, app.TriggerwordsStartsWith, true) 420 } 421 422 // hooks created before we added the ContentType field should be considered as 423 // application/x-www-form-urlencoded 424 func TestCreatePostWithOutgoingHook_no_content_type(t *testing.T) { 425 testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded", "triggerword lorem ipsum", "triggerword", []string{"file_id_1"}, app.TriggerwordsExactMatch, false) 426 testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded", "triggerwordaaazzz lorem ipsum", "triggerword", []string{"file_id_1"}, app.TriggerwordsStartsWith, false) 427 testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded", "triggerword lorem ipsum", "", []string{"file_id_1, file_id_2"}, app.TriggerwordsExactMatch, false) 428 testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded", "triggerwordaaazzz lorem ipsum", "", []string{"file_id_1, file_id_2"}, app.TriggerwordsStartsWith, false) 429 testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded", "triggerword lorem ipsum", "triggerword", []string{"file_id_1"}, app.TriggerwordsExactMatch, true) 430 testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded", "triggerword lorem ipsum", "", []string{"file_id_1, file_id_2"}, app.TriggerwordsExactMatch, true) 431 } 432 433 func TestCreatePostPublic(t *testing.T) { 434 th := Setup(t).InitBasic() 435 defer th.TearDown() 436 Client := th.Client 437 438 post := &model.Post{ChannelId: th.BasicChannel.Id, Message: "#hashtag a" + model.NewId() + "a"} 439 440 user := model.User{Email: th.GenerateTestEmail(), Nickname: "Joram Wilander", Password: "hello1", Username: GenerateTestUsername(), Roles: model.SYSTEM_USER_ROLE_ID} 441 442 ruser, resp := Client.CreateUser(&user) 443 CheckNoError(t, resp) 444 445 Client.Login(user.Email, user.Password) 446 447 _, resp = Client.CreatePost(post) 448 CheckForbiddenStatus(t, resp) 449 450 th.App.UpdateUserRoles(ruser.Id, model.SYSTEM_USER_ROLE_ID+" "+model.SYSTEM_POST_ALL_PUBLIC_ROLE_ID, false) 451 th.App.Srv().InvalidateAllCaches() 452 453 Client.Login(user.Email, user.Password) 454 455 _, resp = Client.CreatePost(post) 456 CheckNoError(t, resp) 457 458 post.ChannelId = th.BasicPrivateChannel.Id 459 _, resp = Client.CreatePost(post) 460 CheckForbiddenStatus(t, resp) 461 462 th.App.UpdateUserRoles(ruser.Id, model.SYSTEM_USER_ROLE_ID, false) 463 th.App.JoinUserToTeam(th.Context, th.BasicTeam, ruser, "") 464 th.App.UpdateTeamMemberRoles(th.BasicTeam.Id, ruser.Id, model.TEAM_USER_ROLE_ID+" "+model.TEAM_POST_ALL_PUBLIC_ROLE_ID) 465 th.App.Srv().InvalidateAllCaches() 466 467 Client.Login(user.Email, user.Password) 468 469 post.ChannelId = th.BasicPrivateChannel.Id 470 _, resp = Client.CreatePost(post) 471 CheckForbiddenStatus(t, resp) 472 473 post.ChannelId = th.BasicChannel.Id 474 _, resp = Client.CreatePost(post) 475 CheckNoError(t, resp) 476 } 477 478 func TestCreatePostAll(t *testing.T) { 479 th := Setup(t).InitBasic() 480 defer th.TearDown() 481 Client := th.Client 482 483 post := &model.Post{ChannelId: th.BasicChannel.Id, Message: "#hashtag a" + model.NewId() + "a"} 484 485 user := model.User{Email: th.GenerateTestEmail(), Nickname: "Joram Wilander", Password: "hello1", Username: GenerateTestUsername(), Roles: model.SYSTEM_USER_ROLE_ID} 486 487 directChannel, _ := th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, th.BasicUser2.Id) 488 489 ruser, resp := Client.CreateUser(&user) 490 CheckNoError(t, resp) 491 492 Client.Login(user.Email, user.Password) 493 494 _, resp = Client.CreatePost(post) 495 CheckForbiddenStatus(t, resp) 496 497 th.App.UpdateUserRoles(ruser.Id, model.SYSTEM_USER_ROLE_ID+" "+model.SYSTEM_POST_ALL_ROLE_ID, false) 498 th.App.Srv().InvalidateAllCaches() 499 500 Client.Login(user.Email, user.Password) 501 502 _, resp = Client.CreatePost(post) 503 CheckNoError(t, resp) 504 505 post.ChannelId = th.BasicPrivateChannel.Id 506 _, resp = Client.CreatePost(post) 507 CheckNoError(t, resp) 508 509 post.ChannelId = directChannel.Id 510 _, resp = Client.CreatePost(post) 511 CheckNoError(t, resp) 512 513 th.App.UpdateUserRoles(ruser.Id, model.SYSTEM_USER_ROLE_ID, false) 514 th.App.JoinUserToTeam(th.Context, th.BasicTeam, ruser, "") 515 th.App.UpdateTeamMemberRoles(th.BasicTeam.Id, ruser.Id, model.TEAM_USER_ROLE_ID+" "+model.TEAM_POST_ALL_ROLE_ID) 516 th.App.Srv().InvalidateAllCaches() 517 518 Client.Login(user.Email, user.Password) 519 520 post.ChannelId = th.BasicPrivateChannel.Id 521 _, resp = Client.CreatePost(post) 522 CheckNoError(t, resp) 523 524 post.ChannelId = th.BasicChannel.Id 525 _, resp = Client.CreatePost(post) 526 CheckNoError(t, resp) 527 528 post.ChannelId = directChannel.Id 529 _, resp = Client.CreatePost(post) 530 CheckForbiddenStatus(t, resp) 531 } 532 533 func TestCreatePostSendOutOfChannelMentions(t *testing.T) { 534 th := Setup(t).InitBasic() 535 defer th.TearDown() 536 Client := th.Client 537 538 WebSocketClient, err := th.CreateWebSocketClient() 539 require.Nil(t, err) 540 WebSocketClient.Listen() 541 542 inChannelUser := th.CreateUser() 543 th.LinkUserToTeam(inChannelUser, th.BasicTeam) 544 th.App.AddUserToChannel(inChannelUser, th.BasicChannel, false) 545 546 post1 := &model.Post{ChannelId: th.BasicChannel.Id, Message: "@" + inChannelUser.Username} 547 _, resp := Client.CreatePost(post1) 548 CheckNoError(t, resp) 549 CheckCreatedStatus(t, resp) 550 551 timeout := time.After(300 * time.Millisecond) 552 waiting := true 553 for waiting { 554 select { 555 case event := <-WebSocketClient.EventChannel: 556 require.NotEqual(t, model.WEBSOCKET_EVENT_EPHEMERAL_MESSAGE, event.EventType(), "should not have ephemeral message event") 557 case <-timeout: 558 waiting = false 559 } 560 } 561 562 outOfChannelUser := th.CreateUser() 563 th.LinkUserToTeam(outOfChannelUser, th.BasicTeam) 564 565 post2 := &model.Post{ChannelId: th.BasicChannel.Id, Message: "@" + outOfChannelUser.Username} 566 _, resp = Client.CreatePost(post2) 567 CheckNoError(t, resp) 568 CheckCreatedStatus(t, resp) 569 570 timeout = time.After(300 * time.Millisecond) 571 waiting = true 572 for waiting { 573 select { 574 case event := <-WebSocketClient.EventChannel: 575 if event.EventType() != model.WEBSOCKET_EVENT_EPHEMERAL_MESSAGE { 576 // Ignore any other events 577 continue 578 } 579 580 wpost := model.PostFromJson(strings.NewReader(event.GetData()["post"].(string))) 581 582 acm, ok := wpost.GetProp(model.PROPS_ADD_CHANNEL_MEMBER).(map[string]interface{}) 583 require.True(t, ok, "should have received ephemeral post with 'add_channel_member' in props") 584 require.True(t, acm["post_id"] != nil, "should not be nil") 585 require.True(t, acm["user_ids"] != nil, "should not be nil") 586 require.True(t, acm["usernames"] != nil, "should not be nil") 587 waiting = false 588 case <-timeout: 589 require.FailNow(t, "timed out waiting for ephemeral message event") 590 } 591 } 592 } 593 594 func TestCreatePostCheckOnlineStatus(t *testing.T) { 595 th := Setup(t).InitBasic() 596 defer th.TearDown() 597 598 api := Init(th.App, th.Server.Router) 599 session, _ := th.App.GetSession(th.Client.AuthToken) 600 601 cli := th.CreateClient() 602 _, loginResp := cli.Login(th.BasicUser2.Username, th.BasicUser2.Password) 603 require.Nil(t, loginResp.Error) 604 605 wsClient, err := th.CreateWebSocketClientWithClient(cli) 606 require.Nil(t, err) 607 defer wsClient.Close() 608 609 wsClient.Listen() 610 611 waitForEvent := func(isSetOnline bool) { 612 timeout := time.After(5 * time.Second) 613 for { 614 select { 615 case ev := <-wsClient.EventChannel: 616 if ev.EventType() == model.WEBSOCKET_EVENT_POSTED { 617 assert.True(t, ev.GetData()["set_online"].(bool) == isSetOnline) 618 return 619 } 620 case <-timeout: 621 // We just skip the test instead of failing because waiting for more than 5 seconds 622 // to get a response does not make sense, and it will unnecessarily slow down 623 // the tests further in an already congested CI environment. 624 t.Skip("timed out waiting for event") 625 } 626 } 627 } 628 629 handler := api.ApiHandler(createPost) 630 resp := httptest.NewRecorder() 631 post := &model.Post{ 632 ChannelId: th.BasicChannel.Id, 633 Message: "some message", 634 } 635 636 req := httptest.NewRequest("POST", "/api/v4/posts?set_online=false", strings.NewReader(post.ToJson())) 637 req.Header.Set(model.HEADER_AUTH, "Bearer "+session.Token) 638 639 handler.ServeHTTP(resp, req) 640 assert.Equal(t, http.StatusCreated, resp.Code) 641 waitForEvent(false) 642 643 _, err = th.App.GetStatus(th.BasicUser.Id) 644 require.NotNil(t, err) 645 assert.Equal(t, "app.status.get.missing.app_error", err.Id) 646 647 req = httptest.NewRequest("POST", "/api/v4/posts", strings.NewReader(post.ToJson())) 648 req.Header.Set(model.HEADER_AUTH, "Bearer "+session.Token) 649 650 handler.ServeHTTP(resp, req) 651 assert.Equal(t, http.StatusCreated, resp.Code) 652 waitForEvent(true) 653 654 st, err := th.App.GetStatus(th.BasicUser.Id) 655 require.Nil(t, err) 656 assert.Equal(t, "online", st.Status) 657 } 658 659 func TestUpdatePost(t *testing.T) { 660 th := Setup(t).InitBasic() 661 defer th.TearDown() 662 Client := th.Client 663 channel := th.BasicChannel 664 665 th.App.Srv().SetLicense(model.NewTestLicense()) 666 667 fileIds := make([]string, 3) 668 data, err := testutils.ReadTestFile("test.png") 669 require.NoError(t, err) 670 for i := 0; i < len(fileIds); i++ { 671 fileResp, resp := Client.UploadFile(data, channel.Id, "test.png") 672 CheckNoError(t, resp) 673 fileIds[i] = fileResp.FileInfos[0].Id 674 } 675 676 rpost, appErr := th.App.CreatePost(th.Context, &model.Post{ 677 UserId: th.BasicUser.Id, 678 ChannelId: channel.Id, 679 Message: "zz" + model.NewId() + "a", 680 FileIds: fileIds, 681 }, channel, false, true) 682 require.Nil(t, appErr) 683 684 assert.Equal(t, rpost.Message, rpost.Message, "full name didn't match") 685 assert.EqualValues(t, 0, rpost.EditAt, "Newly created post shouldn't have EditAt set") 686 assert.Equal(t, model.StringArray(fileIds), rpost.FileIds, "FileIds should have been set") 687 688 t.Run("same message, fewer files", func(t *testing.T) { 689 msg := "zz" + model.NewId() + " update post" 690 rpost.Message = msg 691 rpost.UserId = "" 692 693 rupost, resp := Client.UpdatePost(rpost.Id, &model.Post{ 694 Id: rpost.Id, 695 Message: rpost.Message, 696 FileIds: fileIds[0:2], // one fewer file id 697 }) 698 CheckNoError(t, resp) 699 700 assert.Equal(t, rupost.Message, msg, "failed to updates") 701 assert.NotEqual(t, 0, rupost.EditAt, "EditAt not updated for post") 702 assert.Equal(t, model.StringArray(fileIds), rupost.FileIds, "FileIds should have not have been updated") 703 704 actual, resp := Client.GetPost(rpost.Id, "") 705 CheckNoError(t, resp) 706 707 assert.Equal(t, actual.Message, msg, "failed to updates") 708 assert.NotEqual(t, 0, actual.EditAt, "EditAt not updated for post") 709 assert.Equal(t, model.StringArray(fileIds), actual.FileIds, "FileIds should have not have been updated") 710 }) 711 712 t.Run("new message, invalid props", func(t *testing.T) { 713 msg1 := "#hashtag a" + model.NewId() + " update post again" 714 rpost.Message = msg1 715 rpost.AddProp(model.PROPS_ADD_CHANNEL_MEMBER, "no good") 716 rrupost, resp := Client.UpdatePost(rpost.Id, rpost) 717 CheckNoError(t, resp) 718 719 assert.Equal(t, msg1, rrupost.Message, "failed to update message") 720 assert.Equal(t, "#hashtag", rrupost.Hashtags, "failed to update hashtags") 721 assert.Nil(t, rrupost.GetProp(model.PROPS_ADD_CHANNEL_MEMBER), "failed to sanitize Props['add_channel_member'], should be nil") 722 723 actual, resp := Client.GetPost(rpost.Id, "") 724 CheckNoError(t, resp) 725 726 assert.Equal(t, msg1, actual.Message, "failed to update message") 727 assert.Equal(t, "#hashtag", actual.Hashtags, "failed to update hashtags") 728 assert.Nil(t, actual.GetProp(model.PROPS_ADD_CHANNEL_MEMBER), "failed to sanitize Props['add_channel_member'], should be nil") 729 }) 730 731 t.Run("join/leave post", func(t *testing.T) { 732 rpost2, err := th.App.CreatePost(th.Context, &model.Post{ 733 ChannelId: channel.Id, 734 Message: "zz" + model.NewId() + "a", 735 Type: model.POST_JOIN_LEAVE, 736 UserId: th.BasicUser.Id, 737 }, channel, false, true) 738 require.Nil(t, err) 739 740 up2 := &model.Post{ 741 Id: rpost2.Id, 742 ChannelId: channel.Id, 743 Message: "zz" + model.NewId() + " update post 2", 744 } 745 _, resp := Client.UpdatePost(rpost2.Id, up2) 746 CheckBadRequestStatus(t, resp) 747 }) 748 749 rpost3, appErr := th.App.CreatePost(th.Context, &model.Post{ 750 ChannelId: channel.Id, 751 Message: "zz" + model.NewId() + "a", 752 UserId: th.BasicUser.Id, 753 }, channel, false, true) 754 require.Nil(t, appErr) 755 756 t.Run("new message, add files", func(t *testing.T) { 757 up3 := &model.Post{ 758 Id: rpost3.Id, 759 ChannelId: channel.Id, 760 Message: "zz" + model.NewId() + " update post 3", 761 FileIds: fileIds[0:2], 762 } 763 rrupost3, resp := Client.UpdatePost(rpost3.Id, up3) 764 CheckNoError(t, resp) 765 assert.Empty(t, rrupost3.FileIds) 766 767 actual, resp := Client.GetPost(rpost.Id, "") 768 CheckNoError(t, resp) 769 assert.Equal(t, model.StringArray(fileIds), actual.FileIds) 770 }) 771 772 t.Run("add slack attachments", func(t *testing.T) { 773 up4 := &model.Post{ 774 Id: rpost3.Id, 775 ChannelId: channel.Id, 776 Message: "zz" + model.NewId() + " update post 3", 777 } 778 up4.AddProp("attachments", []model.SlackAttachment{ 779 { 780 Text: "Hello World", 781 }, 782 }) 783 rrupost3, resp := Client.UpdatePost(rpost3.Id, up4) 784 CheckNoError(t, resp) 785 assert.NotEqual(t, rpost3.EditAt, rrupost3.EditAt) 786 assert.NotEqual(t, rpost3.Attachments(), rrupost3.Attachments()) 787 }) 788 789 t.Run("logged out", func(t *testing.T) { 790 Client.Logout() 791 _, resp := Client.UpdatePost(rpost.Id, rpost) 792 CheckUnauthorizedStatus(t, resp) 793 }) 794 795 t.Run("different user", func(t *testing.T) { 796 th.LoginBasic2() 797 _, resp := Client.UpdatePost(rpost.Id, rpost) 798 CheckForbiddenStatus(t, resp) 799 800 Client.Logout() 801 }) 802 803 t.Run("different user, but team admin", func(t *testing.T) { 804 th.LoginTeamAdmin() 805 _, resp := Client.UpdatePost(rpost.Id, rpost) 806 CheckForbiddenStatus(t, resp) 807 808 Client.Logout() 809 }) 810 811 t.Run("different user, but system admin", func(t *testing.T) { 812 _, resp := th.SystemAdminClient.UpdatePost(rpost.Id, rpost) 813 CheckNoError(t, resp) 814 }) 815 } 816 817 func TestUpdateOthersPostInDirectMessageChannel(t *testing.T) { 818 // This test checks that a sysadmin with the "EDIT_OTHERS_POSTS" permission can edit someone else's post in a 819 // channel without a team (DM/GM). This indirectly checks for the proper cascading all the way to system-wide roles 820 // on the user object of permissions based on a post in a channel with no team ID. 821 th := Setup(t).InitBasic() 822 defer th.TearDown() 823 824 dmChannel := th.CreateDmChannel(th.SystemAdminUser) 825 826 post := &model.Post{ 827 Message: "asd", 828 ChannelId: dmChannel.Id, 829 PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()), 830 UserId: th.BasicUser.Id, 831 CreateAt: 0, 832 } 833 834 post, resp := th.Client.CreatePost(post) 835 CheckNoError(t, resp) 836 837 post.Message = "changed" 838 post, resp = th.SystemAdminClient.UpdatePost(post.Id, post) 839 CheckNoError(t, resp) 840 } 841 842 func TestPatchPost(t *testing.T) { 843 th := Setup(t).InitBasic() 844 defer th.TearDown() 845 Client := th.Client 846 channel := th.BasicChannel 847 848 th.App.Srv().SetLicense(model.NewTestLicense()) 849 850 fileIds := make([]string, 3) 851 data, err := testutils.ReadTestFile("test.png") 852 require.NoError(t, err) 853 for i := 0; i < len(fileIds); i++ { 854 fileResp, resp := Client.UploadFile(data, channel.Id, "test.png") 855 CheckNoError(t, resp) 856 fileIds[i] = fileResp.FileInfos[0].Id 857 } 858 859 post := &model.Post{ 860 ChannelId: channel.Id, 861 IsPinned: true, 862 Message: "#hashtag a message", 863 Props: model.StringInterface{"channel_header": "old_header"}, 864 FileIds: fileIds[0:2], 865 HasReactions: true, 866 } 867 post, _ = Client.CreatePost(post) 868 869 var rpost *model.Post 870 t.Run("new message, props, files, HasReactions bit", func(t *testing.T) { 871 patch := &model.PostPatch{} 872 873 patch.IsPinned = model.NewBool(false) 874 patch.Message = model.NewString("#otherhashtag other message") 875 patch.Props = &model.StringInterface{"channel_header": "new_header"} 876 patchFileIds := model.StringArray(fileIds) // one extra file 877 patch.FileIds = &patchFileIds 878 patch.HasReactions = model.NewBool(false) 879 880 var resp *model.Response 881 rpost, resp = Client.PatchPost(post.Id, patch) 882 CheckNoError(t, resp) 883 884 assert.False(t, rpost.IsPinned, "IsPinned did not update properly") 885 assert.Equal(t, "#otherhashtag other message", rpost.Message, "Message did not update properly") 886 assert.Equal(t, *patch.Props, rpost.GetProps(), "Props did not update properly") 887 assert.Equal(t, "#otherhashtag", rpost.Hashtags, "Message did not update properly") 888 assert.Equal(t, model.StringArray(fileIds[0:2]), rpost.FileIds, "FileIds should not update") 889 assert.False(t, rpost.HasReactions, "HasReactions did not update properly") 890 }) 891 892 t.Run("add slack attachments", func(t *testing.T) { 893 patch2 := &model.PostPatch{} 894 attachments := []model.SlackAttachment{ 895 { 896 Text: "Hello World", 897 }, 898 } 899 patch2.Props = &model.StringInterface{"attachments": attachments} 900 901 rpost2, resp := Client.PatchPost(post.Id, patch2) 902 CheckNoError(t, resp) 903 assert.NotEmpty(t, rpost2.GetProp("attachments")) 904 assert.NotEqual(t, rpost.EditAt, rpost2.EditAt) 905 }) 906 907 t.Run("invalid requests", func(t *testing.T) { 908 r, err := Client.DoApiPut("/posts/"+post.Id+"/patch", "garbage") 909 require.EqualError(t, err, ": Invalid or missing post in request body., ") 910 require.Equal(t, http.StatusBadRequest, r.StatusCode, "wrong status code") 911 912 patch := &model.PostPatch{} 913 _, resp := Client.PatchPost("junk", patch) 914 CheckBadRequestStatus(t, resp) 915 }) 916 917 t.Run("unknown post", func(t *testing.T) { 918 patch := &model.PostPatch{} 919 _, resp := Client.PatchPost(GenerateTestId(), patch) 920 CheckForbiddenStatus(t, resp) 921 }) 922 923 t.Run("logged out", func(t *testing.T) { 924 Client.Logout() 925 patch := &model.PostPatch{} 926 _, resp := Client.PatchPost(post.Id, patch) 927 CheckUnauthorizedStatus(t, resp) 928 }) 929 930 t.Run("different user", func(t *testing.T) { 931 th.LoginBasic2() 932 patch := &model.PostPatch{} 933 _, resp := Client.PatchPost(post.Id, patch) 934 CheckForbiddenStatus(t, resp) 935 }) 936 937 t.Run("different user, but team admin", func(t *testing.T) { 938 th.LoginTeamAdmin() 939 patch := &model.PostPatch{} 940 _, resp := Client.PatchPost(post.Id, patch) 941 CheckForbiddenStatus(t, resp) 942 }) 943 944 t.Run("different user, but system admin", func(t *testing.T) { 945 patch := &model.PostPatch{} 946 _, resp := th.SystemAdminClient.PatchPost(post.Id, patch) 947 CheckNoError(t, resp) 948 }) 949 950 t.Run("edit others posts permission can function independently of edit own post", func(t *testing.T) { 951 th.LoginBasic2() 952 patch := &model.PostPatch{} 953 _, resp := Client.PatchPost(post.Id, patch) 954 CheckForbiddenStatus(t, resp) 955 956 // Add permission to edit others' 957 defer th.RestoreDefaultRolePermissions(th.SaveDefaultRolePermissions()) 958 th.RemovePermissionFromRole(model.PERMISSION_EDIT_POST.Id, model.CHANNEL_USER_ROLE_ID) 959 th.AddPermissionToRole(model.PERMISSION_EDIT_OTHERS_POSTS.Id, model.CHANNEL_USER_ROLE_ID) 960 961 _, resp = Client.PatchPost(post.Id, patch) 962 CheckNoError(t, resp) 963 }) 964 } 965 966 func TestPinPost(t *testing.T) { 967 th := Setup(t).InitBasic() 968 defer th.TearDown() 969 Client := th.Client 970 971 post := th.BasicPost 972 pass, resp := Client.PinPost(post.Id) 973 CheckNoError(t, resp) 974 975 require.True(t, pass, "should have passed") 976 rpost, err := th.App.GetSinglePost(post.Id) 977 require.Nil(t, err) 978 require.True(t, rpost.IsPinned, "failed to pin post") 979 980 pass, resp = Client.PinPost("junk") 981 CheckBadRequestStatus(t, resp) 982 require.False(t, pass, "should have failed") 983 984 _, resp = Client.PinPost(GenerateTestId()) 985 CheckForbiddenStatus(t, resp) 986 987 t.Run("unable-to-pin-post-in-read-only-town-square", func(t *testing.T) { 988 townSquareIsReadOnly := *th.App.Config().TeamSettings.ExperimentalTownSquareIsReadOnly 989 th.App.Srv().SetLicense(model.NewTestLicense()) 990 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.ExperimentalTownSquareIsReadOnly = true }) 991 992 defer th.App.Srv().RemoveLicense() 993 defer th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.ExperimentalTownSquareIsReadOnly = townSquareIsReadOnly }) 994 995 channel, err := th.App.GetChannelByName("town-square", th.BasicTeam.Id, true) 996 assert.Nil(t, err) 997 adminPost := th.CreatePostWithClient(th.SystemAdminClient, channel) 998 999 _, resp = Client.PinPost(adminPost.Id) 1000 CheckForbiddenStatus(t, resp) 1001 }) 1002 1003 Client.Logout() 1004 _, resp = Client.PinPost(post.Id) 1005 CheckUnauthorizedStatus(t, resp) 1006 1007 _, resp = th.SystemAdminClient.PinPost(post.Id) 1008 CheckNoError(t, resp) 1009 } 1010 1011 func TestUnpinPost(t *testing.T) { 1012 th := Setup(t).InitBasic() 1013 defer th.TearDown() 1014 Client := th.Client 1015 1016 pinnedPost := th.CreatePinnedPost() 1017 pass, resp := Client.UnpinPost(pinnedPost.Id) 1018 CheckNoError(t, resp) 1019 require.True(t, pass, "should have passed") 1020 1021 rpost, err := th.App.GetSinglePost(pinnedPost.Id) 1022 require.Nil(t, err) 1023 require.False(t, rpost.IsPinned) 1024 1025 pass, resp = Client.UnpinPost("junk") 1026 CheckBadRequestStatus(t, resp) 1027 require.False(t, pass, "should have failed") 1028 1029 _, resp = Client.UnpinPost(GenerateTestId()) 1030 CheckForbiddenStatus(t, resp) 1031 1032 Client.Logout() 1033 _, resp = Client.UnpinPost(pinnedPost.Id) 1034 CheckUnauthorizedStatus(t, resp) 1035 1036 _, resp = th.SystemAdminClient.UnpinPost(pinnedPost.Id) 1037 CheckNoError(t, resp) 1038 } 1039 1040 func TestGetPostsForChannel(t *testing.T) { 1041 th := Setup(t).InitBasic() 1042 defer th.TearDown() 1043 Client := th.Client 1044 1045 post1 := th.CreatePost() 1046 post2 := th.CreatePost() 1047 post3 := &model.Post{ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a", RootId: post1.Id} 1048 post3, _ = Client.CreatePost(post3) 1049 1050 time.Sleep(300 * time.Millisecond) 1051 since := model.GetMillis() 1052 time.Sleep(300 * time.Millisecond) 1053 1054 post4 := th.CreatePost() 1055 1056 th.TestForAllClients(t, func(t *testing.T, c *model.Client4) { 1057 posts, resp := c.GetPostsForChannel(th.BasicChannel.Id, 0, 60, "", false) 1058 CheckNoError(t, resp) 1059 require.Equal(t, post4.Id, posts.Order[0], "wrong order") 1060 require.Equal(t, post3.Id, posts.Order[1], "wrong order") 1061 require.Equal(t, post2.Id, posts.Order[2], "wrong order") 1062 require.Equal(t, post1.Id, posts.Order[3], "wrong order") 1063 1064 posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 0, 3, resp.Etag, false) 1065 CheckEtag(t, posts, resp) 1066 1067 posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 0, 3, "", false) 1068 CheckNoError(t, resp) 1069 require.Len(t, posts.Order, 3, "wrong number returned") 1070 1071 _, ok := posts.Posts[post3.Id] 1072 require.True(t, ok, "missing comment") 1073 _, ok = posts.Posts[post1.Id] 1074 require.True(t, ok, "missing root post") 1075 1076 posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 1, 1, "", false) 1077 CheckNoError(t, resp) 1078 require.Equal(t, post3.Id, posts.Order[0], "wrong order") 1079 1080 posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 10000, 10000, "", false) 1081 CheckNoError(t, resp) 1082 require.Empty(t, posts.Order, "should be no posts") 1083 }) 1084 1085 post5 := th.CreatePost() 1086 1087 th.TestForAllClients(t, func(t *testing.T, c *model.Client4) { 1088 posts, resp := c.GetPostsSince(th.BasicChannel.Id, since, false) 1089 CheckNoError(t, resp) 1090 require.Len(t, posts.Posts, 2, "should return 2 posts") 1091 1092 // "since" query to return empty NextPostId and PrevPostId 1093 require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId") 1094 require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId") 1095 1096 found := make([]bool, 2) 1097 for _, p := range posts.Posts { 1098 require.LessOrEqual(t, since, p.CreateAt, "bad create at for post returned") 1099 1100 if p.Id == post4.Id { 1101 found[0] = true 1102 } else if p.Id == post5.Id { 1103 found[1] = true 1104 } 1105 } 1106 for _, f := range found { 1107 require.True(t, f, "missing post") 1108 } 1109 1110 _, resp = c.GetPostsForChannel("", 0, 60, "", false) 1111 CheckBadRequestStatus(t, resp) 1112 1113 _, resp = c.GetPostsForChannel("junk", 0, 60, "", false) 1114 CheckBadRequestStatus(t, resp) 1115 }) 1116 1117 _, resp := Client.GetPostsForChannel(model.NewId(), 0, 60, "", false) 1118 CheckForbiddenStatus(t, resp) 1119 1120 Client.Logout() 1121 _, resp = Client.GetPostsForChannel(model.NewId(), 0, 60, "", false) 1122 CheckUnauthorizedStatus(t, resp) 1123 1124 // more tests for next_post_id, prev_post_id, and order 1125 // There are 12 posts composed of first 2 system messages and 10 created posts 1126 Client.Login(th.BasicUser.Email, th.BasicUser.Password) 1127 th.CreatePost() // post6 1128 post7 := th.CreatePost() 1129 post8 := th.CreatePost() 1130 th.CreatePost() // post9 1131 post10 := th.CreatePost() 1132 1133 var posts *model.PostList 1134 th.TestForAllClients(t, func(t *testing.T, c *model.Client4) { 1135 // get the system post IDs posted before the created posts above 1136 posts, resp = c.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 2, "", false) 1137 systemPostId1 := posts.Order[1] 1138 1139 // similar to '/posts' 1140 posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 0, 60, "", false) 1141 CheckNoError(t, resp) 1142 require.Len(t, posts.Order, 12, "expected 12 posts") 1143 require.Equal(t, post10.Id, posts.Order[0], "posts not in order") 1144 require.Equal(t, systemPostId1, posts.Order[11], "posts not in order") 1145 require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId") 1146 require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId") 1147 1148 // similar to '/posts?per_page=3' 1149 posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 0, 3, "", false) 1150 CheckNoError(t, resp) 1151 require.Len(t, posts.Order, 3, "expected 3 posts") 1152 require.Equal(t, post10.Id, posts.Order[0], "posts not in order") 1153 require.Equal(t, post8.Id, posts.Order[2], "should return 3 posts and match order") 1154 require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId") 1155 require.Equal(t, post7.Id, posts.PrevPostId, "should return post7.Id as PrevPostId") 1156 1157 // similar to '/posts?per_page=3&page=1' 1158 posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 1, 3, "", false) 1159 CheckNoError(t, resp) 1160 require.Len(t, posts.Order, 3, "expected 3 posts") 1161 require.Equal(t, post7.Id, posts.Order[0], "posts not in order") 1162 require.Equal(t, post5.Id, posts.Order[2], "posts not in order") 1163 require.Equal(t, post8.Id, posts.NextPostId, "should return post8.Id as NextPostId") 1164 require.Equal(t, post4.Id, posts.PrevPostId, "should return post4.Id as PrevPostId") 1165 1166 // similar to '/posts?per_page=3&page=2' 1167 posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 2, 3, "", false) 1168 CheckNoError(t, resp) 1169 require.Len(t, posts.Order, 3, "expected 3 posts") 1170 require.Equal(t, post4.Id, posts.Order[0], "posts not in order") 1171 require.Equal(t, post2.Id, posts.Order[2], "should return 3 posts and match order") 1172 require.Equal(t, post5.Id, posts.NextPostId, "should return post5.Id as NextPostId") 1173 require.Equal(t, post1.Id, posts.PrevPostId, "should return post1.Id as PrevPostId") 1174 1175 // similar to '/posts?per_page=3&page=3' 1176 posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 3, 3, "", false) 1177 CheckNoError(t, resp) 1178 require.Len(t, posts.Order, 3, "expected 3 posts") 1179 require.Equal(t, post1.Id, posts.Order[0], "posts not in order") 1180 require.Equal(t, systemPostId1, posts.Order[2], "should return 3 posts and match order") 1181 require.Equal(t, post2.Id, posts.NextPostId, "should return post2.Id as NextPostId") 1182 require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId") 1183 1184 // similar to '/posts?per_page=3&page=4' 1185 posts, resp = c.GetPostsForChannel(th.BasicChannel.Id, 4, 3, "", false) 1186 CheckNoError(t, resp) 1187 require.Empty(t, posts.Order, "should return 0 post") 1188 require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId") 1189 require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId") 1190 }) 1191 } 1192 1193 func TestGetFlaggedPostsForUser(t *testing.T) { 1194 th := Setup(t).InitBasic() 1195 defer th.TearDown() 1196 Client := th.Client 1197 user := th.BasicUser 1198 team1 := th.BasicTeam 1199 channel1 := th.BasicChannel 1200 post1 := th.CreatePost() 1201 channel2 := th.CreatePublicChannel() 1202 post2 := th.CreatePostWithClient(Client, channel2) 1203 1204 preference := model.Preference{ 1205 UserId: user.Id, 1206 Category: model.PREFERENCE_CATEGORY_FLAGGED_POST, 1207 Name: post1.Id, 1208 Value: "true", 1209 } 1210 _, resp := Client.UpdatePreferences(user.Id, &model.Preferences{preference}) 1211 CheckNoError(t, resp) 1212 preference.Name = post2.Id 1213 _, resp = Client.UpdatePreferences(user.Id, &model.Preferences{preference}) 1214 CheckNoError(t, resp) 1215 1216 opl := model.NewPostList() 1217 opl.AddPost(post1) 1218 opl.AddOrder(post1.Id) 1219 1220 rpl, resp := Client.GetFlaggedPostsForUserInChannel(user.Id, channel1.Id, 0, 10) 1221 CheckNoError(t, resp) 1222 1223 require.Len(t, rpl.Posts, 1, "should have returned 1 post") 1224 require.Equal(t, opl.Posts, rpl.Posts, "posts should have matched") 1225 1226 rpl, resp = Client.GetFlaggedPostsForUserInChannel(user.Id, channel1.Id, 0, 1) 1227 CheckNoError(t, resp) 1228 require.Len(t, rpl.Posts, 1, "should have returned 1 post") 1229 1230 rpl, resp = Client.GetFlaggedPostsForUserInChannel(user.Id, channel1.Id, 1, 1) 1231 CheckNoError(t, resp) 1232 require.Empty(t, rpl.Posts) 1233 1234 rpl, resp = Client.GetFlaggedPostsForUserInChannel(user.Id, GenerateTestId(), 0, 10) 1235 CheckNoError(t, resp) 1236 require.Empty(t, rpl.Posts) 1237 1238 rpl, resp = Client.GetFlaggedPostsForUserInChannel(user.Id, "junk", 0, 10) 1239 CheckBadRequestStatus(t, resp) 1240 require.Nil(t, rpl) 1241 1242 opl.AddPost(post2) 1243 opl.AddOrder(post2.Id) 1244 1245 rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 0, 10) 1246 CheckNoError(t, resp) 1247 require.Len(t, rpl.Posts, 2, "should have returned 2 posts") 1248 require.Equal(t, opl.Posts, rpl.Posts, "posts should have matched") 1249 1250 rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 0, 1) 1251 CheckNoError(t, resp) 1252 require.Len(t, rpl.Posts, 1, "should have returned 1 post") 1253 1254 rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 1, 1) 1255 CheckNoError(t, resp) 1256 require.Len(t, rpl.Posts, 1, "should have returned 1 post") 1257 1258 rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 1000, 10) 1259 CheckNoError(t, resp) 1260 require.Empty(t, rpl.Posts) 1261 1262 rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, GenerateTestId(), 0, 10) 1263 CheckNoError(t, resp) 1264 require.Empty(t, rpl.Posts) 1265 1266 rpl, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, "junk", 0, 10) 1267 CheckBadRequestStatus(t, resp) 1268 require.Nil(t, rpl) 1269 1270 channel3 := th.CreatePrivateChannel() 1271 post4 := th.CreatePostWithClient(Client, channel3) 1272 1273 preference.Name = post4.Id 1274 Client.UpdatePreferences(user.Id, &model.Preferences{preference}) 1275 1276 opl.AddPost(post4) 1277 opl.AddOrder(post4.Id) 1278 1279 rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 0, 10) 1280 CheckNoError(t, resp) 1281 require.Len(t, rpl.Posts, 3, "should have returned 3 posts") 1282 require.Equal(t, opl.Posts, rpl.Posts, "posts should have matched") 1283 1284 rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 0, 2) 1285 CheckNoError(t, resp) 1286 require.Len(t, rpl.Posts, 2, "should have returned 2 posts") 1287 1288 rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 2, 2) 1289 CheckNoError(t, resp) 1290 require.Len(t, rpl.Posts, 1, "should have returned 1 post") 1291 1292 rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 1000, 10) 1293 CheckNoError(t, resp) 1294 require.Empty(t, rpl.Posts) 1295 1296 channel4 := th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE) 1297 post5 := th.CreatePostWithClient(th.SystemAdminClient, channel4) 1298 1299 preference.Name = post5.Id 1300 _, resp = Client.UpdatePreferences(user.Id, &model.Preferences{preference}) 1301 CheckForbiddenStatus(t, resp) 1302 1303 rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 0, 10) 1304 CheckNoError(t, resp) 1305 require.Len(t, rpl.Posts, 3, "should have returned 3 posts") 1306 require.Equal(t, opl.Posts, rpl.Posts, "posts should have matched") 1307 1308 th.AddUserToChannel(user, channel4) 1309 _, resp = Client.UpdatePreferences(user.Id, &model.Preferences{preference}) 1310 CheckNoError(t, resp) 1311 1312 rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 0, 10) 1313 CheckNoError(t, resp) 1314 1315 opl.AddPost(post5) 1316 opl.AddOrder(post5.Id) 1317 require.Len(t, rpl.Posts, 4, "should have returned 4 posts") 1318 require.Equal(t, opl.Posts, rpl.Posts, "posts should have matched") 1319 1320 err := th.App.RemoveUserFromChannel(th.Context, user.Id, "", channel4) 1321 assert.Nil(t, err, "unable to remove user from channel") 1322 1323 rpl, resp = Client.GetFlaggedPostsForUser(user.Id, 0, 10) 1324 CheckNoError(t, resp) 1325 1326 opl2 := model.NewPostList() 1327 opl2.AddPost(post1) 1328 opl2.AddOrder(post1.Id) 1329 opl2.AddPost(post2) 1330 opl2.AddOrder(post2.Id) 1331 opl2.AddPost(post4) 1332 opl2.AddOrder(post4.Id) 1333 1334 require.Len(t, rpl.Posts, 3, "should have returned 3 posts") 1335 require.Equal(t, opl2.Posts, rpl.Posts, "posts should have matched") 1336 1337 _, resp = Client.GetFlaggedPostsForUser("junk", 0, 10) 1338 CheckBadRequestStatus(t, resp) 1339 1340 _, resp = Client.GetFlaggedPostsForUser(GenerateTestId(), 0, 10) 1341 CheckForbiddenStatus(t, resp) 1342 1343 Client.Logout() 1344 1345 _, resp = Client.GetFlaggedPostsForUserInChannel(user.Id, channel1.Id, 0, 10) 1346 CheckUnauthorizedStatus(t, resp) 1347 1348 _, resp = Client.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 0, 10) 1349 CheckUnauthorizedStatus(t, resp) 1350 1351 _, resp = Client.GetFlaggedPostsForUser(user.Id, 0, 10) 1352 CheckUnauthorizedStatus(t, resp) 1353 1354 _, resp = th.SystemAdminClient.GetFlaggedPostsForUserInChannel(user.Id, channel1.Id, 0, 10) 1355 CheckNoError(t, resp) 1356 1357 _, resp = th.SystemAdminClient.GetFlaggedPostsForUserInTeam(user.Id, team1.Id, 0, 10) 1358 CheckNoError(t, resp) 1359 1360 _, resp = th.SystemAdminClient.GetFlaggedPostsForUser(user.Id, 0, 10) 1361 CheckNoError(t, resp) 1362 1363 mockStore := mocks.Store{} 1364 mockPostStore := mocks.PostStore{} 1365 mockPostStore.On("GetFlaggedPosts", mock.AnythingOfType("string"), mock.AnythingOfType("int"), mock.AnythingOfType("int")).Return(nil, errors.New("some-error")) 1366 mockPostStore.On("ClearCaches").Return() 1367 mockStore.On("Team").Return(th.App.Srv().Store.Team()) 1368 mockStore.On("Channel").Return(th.App.Srv().Store.Channel()) 1369 mockStore.On("User").Return(th.App.Srv().Store.User()) 1370 mockStore.On("Scheme").Return(th.App.Srv().Store.Scheme()) 1371 mockStore.On("Post").Return(&mockPostStore) 1372 mockStore.On("FileInfo").Return(th.App.Srv().Store.FileInfo()) 1373 mockStore.On("Webhook").Return(th.App.Srv().Store.Webhook()) 1374 mockStore.On("System").Return(th.App.Srv().Store.System()) 1375 mockStore.On("License").Return(th.App.Srv().Store.License()) 1376 mockStore.On("Role").Return(th.App.Srv().Store.Role()) 1377 mockStore.On("Close").Return(nil) 1378 th.App.Srv().Store = &mockStore 1379 1380 _, resp = th.SystemAdminClient.GetFlaggedPostsForUser(user.Id, 0, 10) 1381 CheckInternalErrorStatus(t, resp) 1382 } 1383 1384 func TestGetPostsBefore(t *testing.T) { 1385 th := Setup(t).InitBasic() 1386 defer th.TearDown() 1387 Client := th.Client 1388 1389 post1 := th.CreatePost() 1390 post2 := th.CreatePost() 1391 post3 := th.CreatePost() 1392 post4 := th.CreatePost() 1393 post5 := th.CreatePost() 1394 1395 posts, resp := Client.GetPostsBefore(th.BasicChannel.Id, post3.Id, 0, 100, "", false) 1396 CheckNoError(t, resp) 1397 1398 found := make([]bool, 2) 1399 for _, p := range posts.Posts { 1400 if p.Id == post1.Id { 1401 found[0] = true 1402 } else if p.Id == post2.Id { 1403 found[1] = true 1404 } 1405 1406 require.NotEqual(t, post4.Id, p.Id, "returned posts after") 1407 require.NotEqual(t, post5.Id, p.Id, "returned posts after") 1408 } 1409 1410 for _, f := range found { 1411 require.True(t, f, "missing post") 1412 } 1413 1414 require.Equal(t, post3.Id, posts.NextPostId, "should match NextPostId") 1415 require.Equal(t, "", posts.PrevPostId, "should match empty PrevPostId") 1416 1417 posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post4.Id, 1, 1, "", false) 1418 CheckNoError(t, resp) 1419 require.Len(t, posts.Posts, 1, "too many posts returned") 1420 require.Equal(t, post2.Id, posts.Order[0], "should match returned post") 1421 require.Equal(t, post3.Id, posts.NextPostId, "should match NextPostId") 1422 require.Equal(t, post1.Id, posts.PrevPostId, "should match PrevPostId") 1423 1424 posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, "junk", 1, 1, "", false) 1425 CheckBadRequestStatus(t, resp) 1426 1427 posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post5.Id, 0, 3, "", false) 1428 CheckNoError(t, resp) 1429 require.Len(t, posts.Posts, 3, "should match length of posts returned") 1430 require.Equal(t, post4.Id, posts.Order[0], "should match returned post") 1431 require.Equal(t, post2.Id, posts.Order[2], "should match returned post") 1432 require.Equal(t, post5.Id, posts.NextPostId, "should match NextPostId") 1433 require.Equal(t, post1.Id, posts.PrevPostId, "should match PrevPostId") 1434 1435 // get the system post IDs posted before the created posts above 1436 posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 2, "", false) 1437 CheckNoError(t, resp) 1438 systemPostId2 := posts.Order[0] 1439 systemPostId1 := posts.Order[1] 1440 1441 posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post5.Id, 1, 3, "", false) 1442 CheckNoError(t, resp) 1443 require.Len(t, posts.Posts, 3, "should match length of posts returned") 1444 require.Equal(t, post1.Id, posts.Order[0], "should match returned post") 1445 require.Equal(t, systemPostId2, posts.Order[1], "should match returned post") 1446 require.Equal(t, systemPostId1, posts.Order[2], "should match returned post") 1447 require.Equal(t, post2.Id, posts.NextPostId, "should match NextPostId") 1448 require.Equal(t, "", posts.PrevPostId, "should return empty PrevPostId") 1449 1450 // more tests for next_post_id, prev_post_id, and order 1451 // There are 12 posts composed of first 2 system messages and 10 created posts 1452 post6 := th.CreatePost() 1453 th.CreatePost() // post7 1454 post8 := th.CreatePost() 1455 post9 := th.CreatePost() 1456 th.CreatePost() // post10 1457 1458 // similar to '/posts?before=post9' 1459 posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 0, 60, "", false) 1460 CheckNoError(t, resp) 1461 require.Len(t, posts.Order, 10, "expected 10 posts") 1462 require.Equal(t, post8.Id, posts.Order[0], "posts not in order") 1463 require.Equal(t, systemPostId1, posts.Order[9], "posts not in order") 1464 require.Equal(t, post9.Id, posts.NextPostId, "should return post9.Id as NextPostId") 1465 require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId") 1466 1467 // similar to '/posts?before=post9&per_page=3' 1468 posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 0, 3, "", false) 1469 CheckNoError(t, resp) 1470 require.Len(t, posts.Order, 3, "expected 3 posts") 1471 require.Equal(t, post8.Id, posts.Order[0], "posts not in order") 1472 require.Equal(t, post6.Id, posts.Order[2], "should return 3 posts and match order") 1473 require.Equal(t, post9.Id, posts.NextPostId, "should return post9.Id as NextPostId") 1474 require.Equal(t, post5.Id, posts.PrevPostId, "should return post5.Id as PrevPostId") 1475 1476 // similar to '/posts?before=post9&per_page=3&page=1' 1477 posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 1, 3, "", false) 1478 CheckNoError(t, resp) 1479 require.Len(t, posts.Order, 3, "expected 3 posts") 1480 require.Equal(t, post5.Id, posts.Order[0], "posts not in order") 1481 require.Equal(t, post3.Id, posts.Order[2], "posts not in order") 1482 require.Equal(t, post6.Id, posts.NextPostId, "should return post6.Id as NextPostId") 1483 require.Equal(t, post2.Id, posts.PrevPostId, "should return post2.Id as PrevPostId") 1484 1485 // similar to '/posts?before=post9&per_page=3&page=2' 1486 posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post9.Id, 2, 3, "", false) 1487 CheckNoError(t, resp) 1488 require.Len(t, posts.Order, 3, "expected 3 posts") 1489 require.Equal(t, post2.Id, posts.Order[0], "posts not in order") 1490 require.Equal(t, systemPostId2, posts.Order[2], "posts not in order") 1491 require.Equal(t, post3.Id, posts.NextPostId, "should return post3.Id as NextPostId") 1492 require.Equal(t, systemPostId1, posts.PrevPostId, "should return systemPostId1 as PrevPostId") 1493 1494 // similar to '/posts?before=post1&per_page=3' 1495 posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 3, "", false) 1496 CheckNoError(t, resp) 1497 require.Len(t, posts.Order, 2, "expected 2 posts") 1498 require.Equal(t, systemPostId2, posts.Order[0], "posts not in order") 1499 require.Equal(t, systemPostId1, posts.Order[1], "posts not in order") 1500 require.Equal(t, post1.Id, posts.NextPostId, "should return post1.Id as NextPostId") 1501 require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId") 1502 1503 // similar to '/posts?before=systemPostId1' 1504 posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, systemPostId1, 0, 60, "", false) 1505 CheckNoError(t, resp) 1506 require.Empty(t, posts.Order, "should return 0 post") 1507 require.Equal(t, systemPostId1, posts.NextPostId, "should return systemPostId1 as NextPostId") 1508 require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId") 1509 1510 // similar to '/posts?before=systemPostId1&per_page=60&page=1' 1511 posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, systemPostId1, 1, 60, "", false) 1512 CheckNoError(t, resp) 1513 require.Empty(t, posts.Order, "should return 0 posts") 1514 require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId") 1515 require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId") 1516 1517 // similar to '/posts?before=non-existent-post' 1518 nonExistentPostId := model.NewId() 1519 posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, nonExistentPostId, 0, 60, "", false) 1520 CheckNoError(t, resp) 1521 require.Empty(t, posts.Order, "should return 0 post") 1522 require.Equal(t, nonExistentPostId, posts.NextPostId, "should return nonExistentPostId as NextPostId") 1523 require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId") 1524 } 1525 1526 func TestGetPostsAfter(t *testing.T) { 1527 th := Setup(t).InitBasic() 1528 defer th.TearDown() 1529 Client := th.Client 1530 1531 post1 := th.CreatePost() 1532 post2 := th.CreatePost() 1533 post3 := th.CreatePost() 1534 post4 := th.CreatePost() 1535 post5 := th.CreatePost() 1536 1537 posts, resp := Client.GetPostsAfter(th.BasicChannel.Id, post3.Id, 0, 100, "", false) 1538 CheckNoError(t, resp) 1539 1540 found := make([]bool, 2) 1541 for _, p := range posts.Posts { 1542 if p.Id == post4.Id { 1543 found[0] = true 1544 } else if p.Id == post5.Id { 1545 found[1] = true 1546 } 1547 require.NotEqual(t, post1.Id, p.Id, "returned posts before") 1548 require.NotEqual(t, post2.Id, p.Id, "returned posts before") 1549 } 1550 1551 for _, f := range found { 1552 require.True(t, f, "missing post") 1553 } 1554 require.Equal(t, "", posts.NextPostId, "should match empty NextPostId") 1555 require.Equal(t, post3.Id, posts.PrevPostId, "should match PrevPostId") 1556 1557 posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 1, 1, "", false) 1558 CheckNoError(t, resp) 1559 require.Len(t, posts.Posts, 1, "too many posts returned") 1560 require.Equal(t, post4.Id, posts.Order[0], "should match returned post") 1561 require.Equal(t, post5.Id, posts.NextPostId, "should match NextPostId") 1562 require.Equal(t, post3.Id, posts.PrevPostId, "should match PrevPostId") 1563 1564 posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, "junk", 1, 1, "", false) 1565 CheckBadRequestStatus(t, resp) 1566 1567 posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post1.Id, 0, 3, "", false) 1568 CheckNoError(t, resp) 1569 require.Len(t, posts.Posts, 3, "should match length of posts returned") 1570 require.Equal(t, post4.Id, posts.Order[0], "should match returned post") 1571 require.Equal(t, post2.Id, posts.Order[2], "should match returned post") 1572 require.Equal(t, post5.Id, posts.NextPostId, "should match NextPostId") 1573 require.Equal(t, post1.Id, posts.PrevPostId, "should match PrevPostId") 1574 1575 posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post1.Id, 1, 3, "", false) 1576 CheckNoError(t, resp) 1577 require.Len(t, posts.Posts, 1, "should match length of posts returned") 1578 require.Equal(t, post5.Id, posts.Order[0], "should match returned post") 1579 require.Equal(t, "", posts.NextPostId, "should match NextPostId") 1580 require.Equal(t, post4.Id, posts.PrevPostId, "should match PrevPostId") 1581 1582 // more tests for next_post_id, prev_post_id, and order 1583 // There are 12 posts composed of first 2 system messages and 10 created posts 1584 post6 := th.CreatePost() 1585 th.CreatePost() // post7 1586 post8 := th.CreatePost() 1587 post9 := th.CreatePost() 1588 post10 := th.CreatePost() 1589 1590 // similar to '/posts?after=post2' 1591 posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 0, 60, "", false) 1592 CheckNoError(t, resp) 1593 require.Len(t, posts.Order, 8, "expected 8 posts") 1594 require.Equal(t, post10.Id, posts.Order[0], "should match order") 1595 require.Equal(t, post3.Id, posts.Order[7], "should match order") 1596 require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId") 1597 require.Equal(t, post2.Id, posts.PrevPostId, "should return post2.Id as PrevPostId") 1598 1599 // similar to '/posts?after=post2&per_page=3' 1600 posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 0, 3, "", false) 1601 CheckNoError(t, resp) 1602 require.Len(t, posts.Order, 3, "expected 3 posts") 1603 require.Equal(t, post5.Id, posts.Order[0], "should match order") 1604 require.Equal(t, post3.Id, posts.Order[2], "should return 3 posts and match order") 1605 require.Equal(t, post6.Id, posts.NextPostId, "should return post6.Id as NextPostId") 1606 require.Equal(t, post2.Id, posts.PrevPostId, "should return post2.Id as PrevPostId") 1607 1608 // similar to '/posts?after=post2&per_page=3&page=1' 1609 posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 1, 3, "", false) 1610 CheckNoError(t, resp) 1611 require.Len(t, posts.Order, 3, "expected 3 posts") 1612 require.Equal(t, post8.Id, posts.Order[0], "should match order") 1613 require.Equal(t, post6.Id, posts.Order[2], "should match order") 1614 require.Equal(t, post9.Id, posts.NextPostId, "should return post9.Id as NextPostId") 1615 require.Equal(t, post5.Id, posts.PrevPostId, "should return post5.Id as PrevPostId") 1616 1617 // similar to '/posts?after=post2&per_page=3&page=2' 1618 posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post2.Id, 2, 3, "", false) 1619 CheckNoError(t, resp) 1620 require.Len(t, posts.Order, 2, "expected 2 posts") 1621 require.Equal(t, post10.Id, posts.Order[0], "should match order") 1622 require.Equal(t, post9.Id, posts.Order[1], "should match order") 1623 require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId") 1624 require.Equal(t, post8.Id, posts.PrevPostId, "should return post8.Id as PrevPostId") 1625 1626 // similar to '/posts?after=post10' 1627 posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post10.Id, 0, 60, "", false) 1628 CheckNoError(t, resp) 1629 require.Empty(t, posts.Order, "should return 0 post") 1630 require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId") 1631 require.Equal(t, post10.Id, posts.PrevPostId, "should return post10.Id as PrevPostId") 1632 1633 // similar to '/posts?after=post10&page=1' 1634 posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, post10.Id, 1, 60, "", false) 1635 CheckNoError(t, resp) 1636 require.Empty(t, posts.Order, "should return 0 post") 1637 require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId") 1638 require.Equal(t, "", posts.PrevPostId, "should return an empty PrevPostId") 1639 1640 // similar to '/posts?after=non-existent-post' 1641 nonExistentPostId := model.NewId() 1642 posts, resp = Client.GetPostsAfter(th.BasicChannel.Id, nonExistentPostId, 0, 60, "", false) 1643 CheckNoError(t, resp) 1644 require.Empty(t, posts.Order, "should return 0 post") 1645 require.Equal(t, "", posts.NextPostId, "should return an empty NextPostId") 1646 require.Equal(t, nonExistentPostId, posts.PrevPostId, "should return nonExistentPostId as PrevPostId") 1647 } 1648 1649 func TestGetPostsForChannelAroundLastUnread(t *testing.T) { 1650 th := Setup(t).InitBasic() 1651 defer th.TearDown() 1652 Client := th.Client 1653 userId := th.BasicUser.Id 1654 channelId := th.BasicChannel.Id 1655 1656 // 12 posts = 2 systems posts + 10 created posts below 1657 post1 := th.CreatePost() 1658 post2 := th.CreatePost() 1659 post3 := th.CreatePost() 1660 post4 := th.CreatePost() 1661 post5 := th.CreatePost() 1662 replyPost := &model.Post{ChannelId: channelId, Message: model.NewId(), RootId: post4.Id, ParentId: post4.Id} 1663 post6, resp := Client.CreatePost(replyPost) 1664 CheckNoError(t, resp) 1665 post7, resp := Client.CreatePost(replyPost) 1666 CheckNoError(t, resp) 1667 post8, resp := Client.CreatePost(replyPost) 1668 CheckNoError(t, resp) 1669 post9, resp := Client.CreatePost(replyPost) 1670 CheckNoError(t, resp) 1671 post10, resp := Client.CreatePost(replyPost) 1672 CheckNoError(t, resp) 1673 1674 postIdNames := map[string]string{ 1675 post1.Id: "post1", 1676 post2.Id: "post2", 1677 post3.Id: "post3", 1678 post4.Id: "post4", 1679 post5.Id: "post5", 1680 post6.Id: "post6 (reply to post4)", 1681 post7.Id: "post7 (reply to post4)", 1682 post8.Id: "post8 (reply to post4)", 1683 post9.Id: "post9 (reply to post4)", 1684 post10.Id: "post10 (reply to post4)", 1685 } 1686 1687 namePost := func(postId string) string { 1688 name, ok := postIdNames[postId] 1689 if ok { 1690 return name 1691 } 1692 1693 return fmt.Sprintf("unknown (%s)", postId) 1694 } 1695 1696 namePosts := func(postIds []string) []string { 1697 namedPostIds := make([]string, 0, len(postIds)) 1698 for _, postId := range postIds { 1699 namedPostIds = append(namedPostIds, namePost(postId)) 1700 } 1701 1702 return namedPostIds 1703 } 1704 1705 namePostsMap := func(posts map[string]*model.Post) []string { 1706 namedPostIds := make([]string, 0, len(posts)) 1707 for postId := range posts { 1708 namedPostIds = append(namedPostIds, namePost(postId)) 1709 } 1710 sort.Strings(namedPostIds) 1711 1712 return namedPostIds 1713 } 1714 1715 assertPostList := func(t *testing.T, expected, actual *model.PostList) { 1716 t.Helper() 1717 1718 require.Equal(t, namePosts(expected.Order), namePosts(actual.Order), "unexpected post order") 1719 require.Equal(t, namePostsMap(expected.Posts), namePostsMap(actual.Posts), "unexpected posts") 1720 require.Equal(t, namePost(expected.NextPostId), namePost(actual.NextPostId), "unexpected next post id") 1721 require.Equal(t, namePost(expected.PrevPostId), namePost(actual.PrevPostId), "unexpected prev post id") 1722 } 1723 1724 // Setting limit_after to zero should fail with a 400 BadRequest. 1725 posts, resp := Client.GetPostsAroundLastUnread(userId, channelId, 20, 0, false) 1726 require.NotNil(t, resp.Error) 1727 require.Equal(t, "api.context.invalid_url_param.app_error", resp.Error.Id) 1728 require.Equal(t, http.StatusBadRequest, resp.StatusCode) 1729 require.Nil(t, posts) 1730 1731 // All returned posts are all read by the user, since it's created by the user itself. 1732 posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 20, 20, false) 1733 CheckNoError(t, resp) 1734 require.Len(t, posts.Order, 12, "Should return 12 posts only since there's no unread post") 1735 1736 // Set channel member's last viewed to 0. 1737 // All returned posts are latest posts as if all previous posts were already read by the user. 1738 channelMember, err := th.App.Srv().Store.Channel().GetMember(context.Background(), channelId, userId) 1739 require.NoError(t, err) 1740 channelMember.LastViewedAt = 0 1741 _, err = th.App.Srv().Store.Channel().UpdateMember(channelMember) 1742 require.NoError(t, err) 1743 th.App.Srv().Store.Post().InvalidateLastPostTimeCache(channelId) 1744 1745 posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 20, 20, false) 1746 CheckNoError(t, resp) 1747 1748 require.Len(t, posts.Order, 12, "Should return 12 posts only since there's no unread post") 1749 1750 // get the first system post generated before the created posts above 1751 posts, resp = Client.GetPostsBefore(th.BasicChannel.Id, post1.Id, 0, 2, "", false) 1752 CheckNoError(t, resp) 1753 systemPost0 := posts.Posts[posts.Order[0]] 1754 postIdNames[systemPost0.Id] = "system post 0" 1755 systemPost1 := posts.Posts[posts.Order[1]] 1756 postIdNames[systemPost1.Id] = "system post 1" 1757 1758 // Set channel member's last viewed before post1. 1759 channelMember, err = th.App.Srv().Store.Channel().GetMember(context.Background(), channelId, userId) 1760 require.NoError(t, err) 1761 channelMember.LastViewedAt = post1.CreateAt - 1 1762 _, err = th.App.Srv().Store.Channel().UpdateMember(channelMember) 1763 require.NoError(t, err) 1764 th.App.Srv().Store.Post().InvalidateLastPostTimeCache(channelId) 1765 1766 posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 3, 3, false) 1767 CheckNoError(t, resp) 1768 1769 assertPostList(t, &model.PostList{ 1770 Order: []string{post3.Id, post2.Id, post1.Id, systemPost0.Id, systemPost1.Id}, 1771 Posts: map[string]*model.Post{ 1772 systemPost0.Id: systemPost0, 1773 systemPost1.Id: systemPost1, 1774 post1.Id: post1, 1775 post2.Id: post2, 1776 post3.Id: post3, 1777 }, 1778 NextPostId: post4.Id, 1779 PrevPostId: "", 1780 }, posts) 1781 1782 // Set channel member's last viewed before post6. 1783 channelMember, err = th.App.Srv().Store.Channel().GetMember(context.Background(), channelId, userId) 1784 require.NoError(t, err) 1785 channelMember.LastViewedAt = post6.CreateAt - 1 1786 _, err = th.App.Srv().Store.Channel().UpdateMember(channelMember) 1787 require.NoError(t, err) 1788 th.App.Srv().Store.Post().InvalidateLastPostTimeCache(channelId) 1789 1790 posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 3, 3, false) 1791 CheckNoError(t, resp) 1792 1793 assertPostList(t, &model.PostList{ 1794 Order: []string{post8.Id, post7.Id, post6.Id, post5.Id, post4.Id, post3.Id}, 1795 Posts: map[string]*model.Post{ 1796 post3.Id: post3, 1797 post4.Id: post4, 1798 post5.Id: post5, 1799 post6.Id: post6, 1800 post7.Id: post7, 1801 post8.Id: post8, 1802 post9.Id: post9, 1803 post10.Id: post10, 1804 }, 1805 NextPostId: post9.Id, 1806 PrevPostId: post2.Id, 1807 }, posts) 1808 1809 // Set channel member's last viewed before post10. 1810 channelMember, err = th.App.Srv().Store.Channel().GetMember(context.Background(), channelId, userId) 1811 require.NoError(t, err) 1812 channelMember.LastViewedAt = post10.CreateAt - 1 1813 _, err = th.App.Srv().Store.Channel().UpdateMember(channelMember) 1814 require.NoError(t, err) 1815 th.App.Srv().Store.Post().InvalidateLastPostTimeCache(channelId) 1816 1817 posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 3, 3, false) 1818 CheckNoError(t, resp) 1819 1820 assertPostList(t, &model.PostList{ 1821 Order: []string{post10.Id, post9.Id, post8.Id, post7.Id}, 1822 Posts: map[string]*model.Post{ 1823 post4.Id: post4, 1824 post6.Id: post6, 1825 post7.Id: post7, 1826 post8.Id: post8, 1827 post9.Id: post9, 1828 post10.Id: post10, 1829 }, 1830 NextPostId: "", 1831 PrevPostId: post6.Id, 1832 }, posts) 1833 1834 // Set channel member's last viewed equal to post10. 1835 channelMember, err = th.App.Srv().Store.Channel().GetMember(context.Background(), channelId, userId) 1836 require.NoError(t, err) 1837 channelMember.LastViewedAt = post10.CreateAt 1838 _, err = th.App.Srv().Store.Channel().UpdateMember(channelMember) 1839 require.NoError(t, err) 1840 th.App.Srv().Store.Post().InvalidateLastPostTimeCache(channelId) 1841 1842 posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 3, 3, false) 1843 CheckNoError(t, resp) 1844 1845 assertPostList(t, &model.PostList{ 1846 Order: []string{post10.Id, post9.Id, post8.Id}, 1847 Posts: map[string]*model.Post{ 1848 post4.Id: post4, 1849 post6.Id: post6, 1850 post7.Id: post7, 1851 post8.Id: post8, 1852 post9.Id: post9, 1853 post10.Id: post10, 1854 }, 1855 NextPostId: "", 1856 PrevPostId: post7.Id, 1857 }, posts) 1858 1859 // Set channel member's last viewed to just before a new reply to a previous thread, not 1860 // otherwise in the requested window. 1861 post11 := th.CreatePost() 1862 post12, resp := Client.CreatePost(&model.Post{ 1863 ChannelId: channelId, 1864 Message: model.NewId(), 1865 RootId: post4.Id, 1866 ParentId: post4.Id, 1867 }) 1868 CheckNoError(t, resp) 1869 post13 := th.CreatePost() 1870 1871 postIdNames[post11.Id] = "post11" 1872 postIdNames[post12.Id] = "post12 (reply to post4)" 1873 postIdNames[post13.Id] = "post13" 1874 1875 channelMember, err = th.App.Srv().Store.Channel().GetMember(context.Background(), channelId, userId) 1876 require.NoError(t, err) 1877 channelMember.LastViewedAt = post12.CreateAt - 1 1878 _, err = th.App.Srv().Store.Channel().UpdateMember(channelMember) 1879 require.NoError(t, err) 1880 th.App.Srv().Store.Post().InvalidateLastPostTimeCache(channelId) 1881 1882 posts, resp = Client.GetPostsAroundLastUnread(userId, channelId, 1, 2, false) 1883 CheckNoError(t, resp) 1884 1885 assertPostList(t, &model.PostList{ 1886 Order: []string{post13.Id, post12.Id, post11.Id}, 1887 Posts: map[string]*model.Post{ 1888 post4.Id: post4, 1889 post6.Id: post6, 1890 post7.Id: post7, 1891 post8.Id: post8, 1892 post9.Id: post9, 1893 post10.Id: post10, 1894 post11.Id: post11, 1895 post12.Id: post12, 1896 post13.Id: post13, 1897 }, 1898 NextPostId: "", 1899 PrevPostId: post10.Id, 1900 }, posts) 1901 } 1902 1903 func TestGetPost(t *testing.T) { 1904 th := Setup(t).InitBasic() 1905 defer th.TearDown() 1906 // TODO: migrate this entirely to the subtest's client 1907 // once the other methods are migrated too. 1908 Client := th.Client 1909 1910 var privatePost *model.Post 1911 th.TestForAllClients(t, func(t *testing.T, c *model.Client4) { 1912 t.Helper() 1913 1914 post, resp := c.GetPost(th.BasicPost.Id, "") 1915 CheckNoError(t, resp) 1916 1917 require.Equal(t, th.BasicPost.Id, post.Id, "post ids don't match") 1918 1919 post, resp = c.GetPost(th.BasicPost.Id, resp.Etag) 1920 CheckEtag(t, post, resp) 1921 1922 _, resp = c.GetPost("", "") 1923 CheckNotFoundStatus(t, resp) 1924 1925 _, resp = c.GetPost("junk", "") 1926 CheckBadRequestStatus(t, resp) 1927 1928 _, resp = c.GetPost(model.NewId(), "") 1929 CheckNotFoundStatus(t, resp) 1930 1931 Client.RemoveUserFromChannel(th.BasicChannel.Id, th.BasicUser.Id) 1932 1933 // Channel is public, should be able to read post 1934 _, resp = c.GetPost(th.BasicPost.Id, "") 1935 CheckNoError(t, resp) 1936 1937 privatePost = th.CreatePostWithClient(Client, th.BasicPrivateChannel) 1938 1939 _, resp = c.GetPost(privatePost.Id, "") 1940 CheckNoError(t, resp) 1941 }) 1942 1943 Client.RemoveUserFromChannel(th.BasicPrivateChannel.Id, th.BasicUser.Id) 1944 1945 // Channel is private, should not be able to read post 1946 _, resp := Client.GetPost(privatePost.Id, "") 1947 CheckForbiddenStatus(t, resp) 1948 1949 // But local client should. 1950 _, resp = th.LocalClient.GetPost(privatePost.Id, "") 1951 CheckNoError(t, resp) 1952 1953 Client.Logout() 1954 1955 // Normal client should get unauthorized, but local client should get 404. 1956 _, resp = Client.GetPost(model.NewId(), "") 1957 CheckUnauthorizedStatus(t, resp) 1958 1959 _, resp = th.LocalClient.GetPost(model.NewId(), "") 1960 CheckNotFoundStatus(t, resp) 1961 } 1962 1963 func TestDeletePost(t *testing.T) { 1964 th := Setup(t).InitBasic() 1965 defer th.TearDown() 1966 Client := th.Client 1967 1968 _, resp := Client.DeletePost("") 1969 CheckNotFoundStatus(t, resp) 1970 1971 _, resp = Client.DeletePost("junk") 1972 CheckBadRequestStatus(t, resp) 1973 1974 _, resp = Client.DeletePost(th.BasicPost.Id) 1975 CheckForbiddenStatus(t, resp) 1976 1977 Client.Login(th.TeamAdminUser.Email, th.TeamAdminUser.Password) 1978 _, resp = Client.DeletePost(th.BasicPost.Id) 1979 CheckNoError(t, resp) 1980 1981 post := th.CreatePost() 1982 user := th.CreateUser() 1983 1984 Client.Logout() 1985 Client.Login(user.Email, user.Password) 1986 1987 _, resp = Client.DeletePost(post.Id) 1988 CheckForbiddenStatus(t, resp) 1989 1990 Client.Logout() 1991 _, resp = Client.DeletePost(model.NewId()) 1992 CheckUnauthorizedStatus(t, resp) 1993 1994 status, resp := th.SystemAdminClient.DeletePost(post.Id) 1995 require.True(t, status, "post should return status OK") 1996 CheckNoError(t, resp) 1997 } 1998 1999 func TestDeletePostMessage(t *testing.T) { 2000 th := Setup(t).InitBasic() 2001 th.LinkUserToTeam(th.SystemAdminUser, th.BasicTeam) 2002 th.App.AddUserToChannel(th.SystemAdminUser, th.BasicChannel, false) 2003 2004 defer th.TearDown() 2005 2006 testCases := []struct { 2007 description string 2008 client *model.Client4 2009 delete_by interface{} 2010 }{ 2011 {"Do not send delete_by to regular user", th.Client, nil}, 2012 {"Send delete_by to system admin user", th.SystemAdminClient, th.SystemAdminUser.Id}, 2013 } 2014 2015 for _, tc := range testCases { 2016 t.Run(tc.description, func(t *testing.T) { 2017 wsClient, err := th.CreateWebSocketClientWithClient(tc.client) 2018 require.Nil(t, err) 2019 defer wsClient.Close() 2020 2021 wsClient.Listen() 2022 2023 post := th.CreatePost() 2024 2025 status, resp := th.SystemAdminClient.DeletePost(post.Id) 2026 require.True(t, status, "post should return status OK") 2027 CheckNoError(t, resp) 2028 2029 timeout := time.After(5 * time.Second) 2030 2031 for { 2032 select { 2033 case ev := <-wsClient.EventChannel: 2034 if ev.EventType() == model.WEBSOCKET_EVENT_POST_DELETED { 2035 assert.Equal(t, tc.delete_by, ev.GetData()["delete_by"]) 2036 return 2037 } 2038 case <-timeout: 2039 // We just skip the test instead of failing because waiting for more than 5 seconds 2040 // to get a response does not make sense, and it will unnecessarily slow down 2041 // the tests further in an already congested CI environment. 2042 t.Skip("timed out waiting for event") 2043 } 2044 } 2045 }) 2046 } 2047 } 2048 2049 func TestGetPostThread(t *testing.T) { 2050 th := Setup(t).InitBasic() 2051 defer th.TearDown() 2052 Client := th.Client 2053 2054 post := &model.Post{ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a", RootId: th.BasicPost.Id} 2055 post, _ = Client.CreatePost(post) 2056 2057 list, resp := Client.GetPostThread(th.BasicPost.Id, "", false) 2058 CheckNoError(t, resp) 2059 2060 var list2 *model.PostList 2061 list2, resp = Client.GetPostThread(th.BasicPost.Id, resp.Etag, false) 2062 CheckEtag(t, list2, resp) 2063 require.Equal(t, th.BasicPost.Id, list.Order[0], "wrong order") 2064 2065 _, ok := list.Posts[th.BasicPost.Id] 2066 require.True(t, ok, "should have had post") 2067 2068 _, ok = list.Posts[post.Id] 2069 require.True(t, ok, "should have had post") 2070 2071 _, resp = Client.GetPostThread("junk", "", false) 2072 CheckBadRequestStatus(t, resp) 2073 2074 _, resp = Client.GetPostThread(model.NewId(), "", false) 2075 CheckNotFoundStatus(t, resp) 2076 2077 Client.RemoveUserFromChannel(th.BasicChannel.Id, th.BasicUser.Id) 2078 2079 // Channel is public, should be able to read post 2080 _, resp = Client.GetPostThread(th.BasicPost.Id, "", false) 2081 CheckNoError(t, resp) 2082 2083 privatePost := th.CreatePostWithClient(Client, th.BasicPrivateChannel) 2084 2085 _, resp = Client.GetPostThread(privatePost.Id, "", false) 2086 CheckNoError(t, resp) 2087 2088 Client.RemoveUserFromChannel(th.BasicPrivateChannel.Id, th.BasicUser.Id) 2089 2090 // Channel is private, should not be able to read post 2091 _, resp = Client.GetPostThread(privatePost.Id, "", false) 2092 CheckForbiddenStatus(t, resp) 2093 2094 Client.Logout() 2095 _, resp = Client.GetPostThread(model.NewId(), "", false) 2096 CheckUnauthorizedStatus(t, resp) 2097 2098 _, resp = th.SystemAdminClient.GetPostThread(th.BasicPost.Id, "", false) 2099 CheckNoError(t, resp) 2100 } 2101 2102 func TestSearchPosts(t *testing.T) { 2103 th := Setup(t).InitBasic() 2104 defer th.TearDown() 2105 experimentalViewArchivedChannels := *th.App.Config().TeamSettings.ExperimentalViewArchivedChannels 2106 defer func() { 2107 th.App.UpdateConfig(func(cfg *model.Config) { 2108 cfg.TeamSettings.ExperimentalViewArchivedChannels = &experimentalViewArchivedChannels 2109 }) 2110 }() 2111 th.App.UpdateConfig(func(cfg *model.Config) { 2112 *cfg.TeamSettings.ExperimentalViewArchivedChannels = true 2113 }) 2114 2115 th.LoginBasic() 2116 Client := th.Client 2117 2118 message := "search for post1" 2119 _ = th.CreateMessagePost(message) 2120 2121 message = "search for post2" 2122 post2 := th.CreateMessagePost(message) 2123 2124 message = "#hashtag search for post3" 2125 post3 := th.CreateMessagePost(message) 2126 2127 message = "hashtag for post4" 2128 _ = th.CreateMessagePost(message) 2129 2130 archivedChannel := th.CreatePublicChannel() 2131 _ = th.CreateMessagePostWithClient(th.Client, archivedChannel, "#hashtag for post3") 2132 th.Client.DeleteChannel(archivedChannel.Id) 2133 2134 terms := "search" 2135 isOrSearch := false 2136 timezoneOffset := 5 2137 searchParams := model.SearchParameter{ 2138 Terms: &terms, 2139 IsOrSearch: &isOrSearch, 2140 TimeZoneOffset: &timezoneOffset, 2141 } 2142 posts, resp := Client.SearchPostsWithParams(th.BasicTeam.Id, &searchParams) 2143 CheckNoError(t, resp) 2144 require.Len(t, posts.Order, 3, "wrong search") 2145 2146 terms = "search" 2147 page := 0 2148 perPage := 2 2149 searchParams = model.SearchParameter{ 2150 Terms: &terms, 2151 IsOrSearch: &isOrSearch, 2152 TimeZoneOffset: &timezoneOffset, 2153 Page: &page, 2154 PerPage: &perPage, 2155 } 2156 posts2, resp := Client.SearchPostsWithParams(th.BasicTeam.Id, &searchParams) 2157 CheckNoError(t, resp) 2158 // We don't support paging for DB search yet, modify this when we do. 2159 require.Len(t, posts2.Order, 3, "Wrong number of posts") 2160 assert.Equal(t, posts.Order[0], posts2.Order[0]) 2161 assert.Equal(t, posts.Order[1], posts2.Order[1]) 2162 2163 page = 1 2164 searchParams = model.SearchParameter{ 2165 Terms: &terms, 2166 IsOrSearch: &isOrSearch, 2167 TimeZoneOffset: &timezoneOffset, 2168 Page: &page, 2169 PerPage: &perPage, 2170 } 2171 posts2, resp = Client.SearchPostsWithParams(th.BasicTeam.Id, &searchParams) 2172 CheckNoError(t, resp) 2173 // We don't support paging for DB search yet, modify this when we do. 2174 require.Empty(t, posts2.Order, "Wrong number of posts") 2175 2176 posts, resp = Client.SearchPosts(th.BasicTeam.Id, "search", false) 2177 CheckNoError(t, resp) 2178 require.Len(t, posts.Order, 3, "wrong search") 2179 2180 posts, resp = Client.SearchPosts(th.BasicTeam.Id, "post2", false) 2181 CheckNoError(t, resp) 2182 require.Len(t, posts.Order, 1, "wrong number of posts") 2183 require.Equal(t, post2.Id, posts.Order[0], "wrong search") 2184 2185 posts, resp = Client.SearchPosts(th.BasicTeam.Id, "#hashtag", false) 2186 CheckNoError(t, resp) 2187 require.Len(t, posts.Order, 1, "wrong number of posts") 2188 require.Equal(t, post3.Id, posts.Order[0], "wrong search") 2189 2190 terms = "#hashtag" 2191 includeDeletedChannels := true 2192 searchParams = model.SearchParameter{ 2193 Terms: &terms, 2194 IsOrSearch: &isOrSearch, 2195 TimeZoneOffset: &timezoneOffset, 2196 IncludeDeletedChannels: &includeDeletedChannels, 2197 } 2198 posts, resp = Client.SearchPostsWithParams(th.BasicTeam.Id, &searchParams) 2199 CheckNoError(t, resp) 2200 require.Len(t, posts.Order, 2, "wrong search") 2201 2202 th.App.UpdateConfig(func(cfg *model.Config) { 2203 *cfg.TeamSettings.ExperimentalViewArchivedChannels = false 2204 }) 2205 2206 posts, resp = Client.SearchPostsWithParams(th.BasicTeam.Id, &searchParams) 2207 CheckNoError(t, resp) 2208 require.Len(t, posts.Order, 1, "wrong search") 2209 2210 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "*", false) 2211 require.Empty(t, posts.Order, "searching for just * shouldn't return any results") 2212 2213 posts, resp = Client.SearchPosts(th.BasicTeam.Id, "post1 post2", true) 2214 CheckNoError(t, resp) 2215 require.Len(t, posts.Order, 2, "wrong search results") 2216 2217 _, resp = Client.SearchPosts("junk", "#sgtitlereview", false) 2218 CheckBadRequestStatus(t, resp) 2219 2220 _, resp = Client.SearchPosts(model.NewId(), "#sgtitlereview", false) 2221 CheckForbiddenStatus(t, resp) 2222 2223 _, resp = Client.SearchPosts(th.BasicTeam.Id, "", false) 2224 CheckBadRequestStatus(t, resp) 2225 2226 Client.Logout() 2227 _, resp = Client.SearchPosts(th.BasicTeam.Id, "#sgtitlereview", false) 2228 CheckUnauthorizedStatus(t, resp) 2229 } 2230 2231 func TestSearchHashtagPosts(t *testing.T) { 2232 th := Setup(t).InitBasic() 2233 defer th.TearDown() 2234 th.LoginBasic() 2235 Client := th.Client 2236 2237 message := "#sgtitlereview with space" 2238 assert.NotNil(t, th.CreateMessagePost(message)) 2239 2240 message = "#sgtitlereview\n with return" 2241 assert.NotNil(t, th.CreateMessagePost(message)) 2242 2243 message = "no hashtag" 2244 assert.NotNil(t, th.CreateMessagePost(message)) 2245 2246 posts, resp := Client.SearchPosts(th.BasicTeam.Id, "#sgtitlereview", false) 2247 CheckNoError(t, resp) 2248 require.Len(t, posts.Order, 2, "wrong search results") 2249 2250 Client.Logout() 2251 _, resp = Client.SearchPosts(th.BasicTeam.Id, "#sgtitlereview", false) 2252 CheckUnauthorizedStatus(t, resp) 2253 } 2254 2255 func TestSearchPostsInChannel(t *testing.T) { 2256 th := Setup(t).InitBasic() 2257 defer th.TearDown() 2258 th.LoginBasic() 2259 Client := th.Client 2260 2261 channel := th.CreatePublicChannel() 2262 2263 message := "sgtitlereview with space" 2264 _ = th.CreateMessagePost(message) 2265 2266 message = "sgtitlereview\n with return" 2267 _ = th.CreateMessagePostWithClient(Client, th.BasicChannel2, message) 2268 2269 message = "other message with no return" 2270 _ = th.CreateMessagePostWithClient(Client, th.BasicChannel2, message) 2271 2272 message = "other message with no return" 2273 _ = th.CreateMessagePostWithClient(Client, channel, message) 2274 2275 posts, _ := Client.SearchPosts(th.BasicTeam.Id, "channel:", false) 2276 require.Empty(t, posts.Order, "wrong number of posts for search 'channel:'") 2277 2278 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "in:", false) 2279 require.Empty(t, posts.Order, "wrong number of posts for search 'in:'") 2280 2281 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "channel:"+th.BasicChannel.Name, false) 2282 require.Lenf(t, posts.Order, 2, "wrong number of posts returned for search 'channel:%v'", th.BasicChannel.Name) 2283 2284 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "in:"+th.BasicChannel2.Name, false) 2285 require.Lenf(t, posts.Order, 2, "wrong number of posts returned for search 'in:%v'", th.BasicChannel2.Name) 2286 2287 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "channel:"+th.BasicChannel2.Name, false) 2288 require.Lenf(t, posts.Order, 2, "wrong number of posts for search 'channel:%v'", th.BasicChannel2.Name) 2289 2290 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "ChAnNeL:"+th.BasicChannel2.Name, false) 2291 require.Lenf(t, posts.Order, 2, "wrong number of posts for search 'ChAnNeL:%v'", th.BasicChannel2.Name) 2292 2293 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "sgtitlereview", false) 2294 require.Lenf(t, posts.Order, 2, "wrong number of posts for search 'sgtitlereview'") 2295 2296 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "sgtitlereview channel:"+th.BasicChannel.Name, false) 2297 require.Lenf(t, posts.Order, 1, "wrong number of posts for search 'sgtitlereview channel:%v'", th.BasicChannel.Name) 2298 2299 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "sgtitlereview in: "+th.BasicChannel2.Name, false) 2300 require.Lenf(t, posts.Order, 1, "wrong number of posts for search 'sgtitlereview in: %v'", th.BasicChannel2.Name) 2301 2302 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "sgtitlereview channel: "+th.BasicChannel2.Name, false) 2303 require.Lenf(t, posts.Order, 1, "wrong number of posts for search 'sgtitlereview channel: %v'", th.BasicChannel2.Name) 2304 2305 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "channel: "+th.BasicChannel2.Name+" channel: "+channel.Name, false) 2306 require.Lenf(t, posts.Order, 3, "wrong number of posts for 'channel: %v channel: %v'", th.BasicChannel2.Name, channel.Name) 2307 } 2308 2309 func TestSearchPostsFromUser(t *testing.T) { 2310 th := Setup(t).InitBasic() 2311 defer th.TearDown() 2312 Client := th.Client 2313 2314 th.LoginTeamAdmin() 2315 user := th.CreateUser() 2316 th.LinkUserToTeam(user, th.BasicTeam) 2317 th.App.AddUserToChannel(user, th.BasicChannel, false) 2318 th.App.AddUserToChannel(user, th.BasicChannel2, false) 2319 2320 message := "sgtitlereview with space" 2321 _ = th.CreateMessagePost(message) 2322 2323 Client.Logout() 2324 th.LoginBasic2() 2325 2326 message = "sgtitlereview\n with return" 2327 _ = th.CreateMessagePostWithClient(Client, th.BasicChannel2, message) 2328 2329 posts, _ := Client.SearchPosts(th.BasicTeam.Id, "from: "+th.TeamAdminUser.Username, false) 2330 require.Lenf(t, posts.Order, 2, "wrong number of posts for search 'from: %v'", th.TeamAdminUser.Username) 2331 2332 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username, false) 2333 require.Lenf(t, posts.Order, 1, "wrong number of posts for search 'from: %v", th.BasicUser2.Username) 2334 2335 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username+" sgtitlereview", false) 2336 require.Lenf(t, posts.Order, 1, "wrong number of posts for search 'from: %v'", th.BasicUser2.Username) 2337 2338 message = "hullo" 2339 _ = th.CreateMessagePost(message) 2340 2341 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username+" in:"+th.BasicChannel.Name, false) 2342 require.Len(t, posts.Order, 1, "wrong number of posts for search 'from: %v in:", th.BasicUser2.Username, th.BasicChannel.Name) 2343 2344 Client.Login(user.Email, user.Password) 2345 2346 // wait for the join/leave messages to be created for user3 since they're done asynchronously 2347 time.Sleep(100 * time.Millisecond) 2348 2349 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username, false) 2350 require.Lenf(t, posts.Order, 2, "wrong number of posts for search 'from: %v'", th.BasicUser2.Username) 2351 2352 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username+" from: "+user.Username, false) 2353 require.Lenf(t, posts.Order, 2, "wrong number of posts for search 'from: %v from: %v'", th.BasicUser2.Username, user.Username) 2354 2355 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username+" from: "+user.Username+" in:"+th.BasicChannel2.Name, false) 2356 require.Len(t, posts.Order, 1, "wrong number of posts") 2357 2358 message = "coconut" 2359 _ = th.CreateMessagePostWithClient(Client, th.BasicChannel2, message) 2360 2361 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "from: "+th.BasicUser2.Username+" from: "+user.Username+" in:"+th.BasicChannel2.Name+" coconut", false) 2362 require.Len(t, posts.Order, 1, "wrong number of posts") 2363 } 2364 2365 func TestSearchPostsWithDateFlags(t *testing.T) { 2366 th := Setup(t).InitBasic() 2367 defer th.TearDown() 2368 th.LoginBasic() 2369 Client := th.Client 2370 2371 message := "sgtitlereview\n with return" 2372 createDate := time.Date(2018, 8, 1, 5, 0, 0, 0, time.UTC) 2373 _ = th.CreateMessagePostNoClient(th.BasicChannel, message, utils.MillisFromTime(createDate)) 2374 2375 message = "other message with no return" 2376 createDate = time.Date(2018, 8, 2, 5, 0, 0, 0, time.UTC) 2377 _ = th.CreateMessagePostNoClient(th.BasicChannel, message, utils.MillisFromTime(createDate)) 2378 2379 message = "other message with no return" 2380 createDate = time.Date(2018, 8, 3, 5, 0, 0, 0, time.UTC) 2381 _ = th.CreateMessagePostNoClient(th.BasicChannel, message, utils.MillisFromTime(createDate)) 2382 2383 posts, _ := Client.SearchPosts(th.BasicTeam.Id, "return", false) 2384 require.Len(t, posts.Order, 3, "wrong number of posts") 2385 2386 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "on:", false) 2387 require.Empty(t, posts.Order, "wrong number of posts") 2388 2389 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "after:", false) 2390 require.Empty(t, posts.Order, "wrong number of posts") 2391 2392 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "before:", false) 2393 require.Empty(t, posts.Order, "wrong number of posts") 2394 2395 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "on:2018-08-01", false) 2396 require.Len(t, posts.Order, 1, "wrong number of posts") 2397 2398 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "after:2018-08-01", false) 2399 resultCount := 0 2400 for _, post := range posts.Posts { 2401 if post.UserId == th.BasicUser.Id { 2402 resultCount = resultCount + 1 2403 } 2404 } 2405 require.Equal(t, 2, resultCount, "wrong number of posts") 2406 2407 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "before:2018-08-02", false) 2408 require.Len(t, posts.Order, 1, "wrong number of posts") 2409 2410 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "before:2018-08-03 after:2018-08-02", false) 2411 require.Empty(t, posts.Order, "wrong number of posts") 2412 2413 posts, _ = Client.SearchPosts(th.BasicTeam.Id, "before:2018-08-03 after:2018-08-01", false) 2414 require.Len(t, posts.Order, 1, "wrong number of posts") 2415 } 2416 2417 func TestGetFileInfosForPost(t *testing.T) { 2418 th := Setup(t).InitBasic() 2419 defer th.TearDown() 2420 Client := th.Client 2421 2422 fileIds := make([]string, 3) 2423 data, err := testutils.ReadTestFile("test.png") 2424 require.NoError(t, err) 2425 for i := 0; i < 3; i++ { 2426 fileResp, _ := Client.UploadFile(data, th.BasicChannel.Id, "test.png") 2427 fileIds[i] = fileResp.FileInfos[0].Id 2428 } 2429 2430 post := &model.Post{ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a", FileIds: fileIds} 2431 post, _ = Client.CreatePost(post) 2432 2433 infos, resp := Client.GetFileInfosForPost(post.Id, "") 2434 CheckNoError(t, resp) 2435 2436 require.Len(t, infos, 3, "missing file infos") 2437 2438 found := false 2439 for _, info := range infos { 2440 if info.Id == fileIds[0] { 2441 found = true 2442 } 2443 } 2444 2445 require.True(t, found, "missing file info") 2446 2447 infos, resp = Client.GetFileInfosForPost(post.Id, resp.Etag) 2448 CheckEtag(t, infos, resp) 2449 2450 infos, resp = Client.GetFileInfosForPost(th.BasicPost.Id, "") 2451 CheckNoError(t, resp) 2452 2453 require.Empty(t, infos, "should have no file infos") 2454 2455 _, resp = Client.GetFileInfosForPost("junk", "") 2456 CheckBadRequestStatus(t, resp) 2457 2458 _, resp = Client.GetFileInfosForPost(model.NewId(), "") 2459 CheckForbiddenStatus(t, resp) 2460 2461 Client.Logout() 2462 _, resp = Client.GetFileInfosForPost(model.NewId(), "") 2463 CheckUnauthorizedStatus(t, resp) 2464 2465 _, resp = th.SystemAdminClient.GetFileInfosForPost(th.BasicPost.Id, "") 2466 CheckNoError(t, resp) 2467 } 2468 2469 func TestSetChannelUnread(t *testing.T) { 2470 th := Setup(t).InitBasic() 2471 defer th.TearDown() 2472 2473 u1 := th.BasicUser 2474 u2 := th.BasicUser2 2475 s2, _ := th.App.GetSession(th.Client.AuthToken) 2476 th.Client.Login(u1.Email, u1.Password) 2477 c1 := th.BasicChannel 2478 c1toc2 := &model.ChannelView{ChannelId: th.BasicChannel2.Id, PrevChannelId: c1.Id} 2479 now := utils.MillisFromTime(time.Now()) 2480 th.CreateMessagePostNoClient(c1, "AAA", now) 2481 p2 := th.CreateMessagePostNoClient(c1, "BBB", now+10) 2482 th.CreateMessagePostNoClient(c1, "CCC", now+20) 2483 2484 pp1 := th.CreateMessagePostNoClient(th.BasicPrivateChannel, "Sssh!", now) 2485 pp2 := th.CreateMessagePostNoClient(th.BasicPrivateChannel, "You Sssh!", now+10) 2486 require.NotNil(t, pp1) 2487 require.NotNil(t, pp2) 2488 2489 // Ensure that post have been read 2490 unread, err := th.App.GetChannelUnread(c1.Id, u1.Id) 2491 require.Nil(t, err) 2492 require.Equal(t, int64(4), unread.MsgCount) 2493 unread, err = th.App.GetChannelUnread(c1.Id, u2.Id) 2494 require.Nil(t, err) 2495 require.Equal(t, int64(4), unread.MsgCount) 2496 _, err = th.App.ViewChannel(c1toc2, u2.Id, s2.Id, false) 2497 require.Nil(t, err) 2498 unread, err = th.App.GetChannelUnread(c1.Id, u2.Id) 2499 require.Nil(t, err) 2500 require.Equal(t, int64(0), unread.MsgCount) 2501 2502 t.Run("Unread last one", func(t *testing.T) { 2503 r := th.Client.SetPostUnread(u1.Id, p2.Id, true) 2504 checkHTTPStatus(t, r, 200, false) 2505 unread, err := th.App.GetChannelUnread(c1.Id, u1.Id) 2506 require.Nil(t, err) 2507 assert.Equal(t, int64(2), unread.MsgCount) 2508 }) 2509 2510 t.Run("Unread on a private channel", func(t *testing.T) { 2511 r := th.Client.SetPostUnread(u1.Id, pp2.Id, true) 2512 assert.Equal(t, 200, r.StatusCode) 2513 unread, err := th.App.GetChannelUnread(th.BasicPrivateChannel.Id, u1.Id) 2514 require.Nil(t, err) 2515 assert.Equal(t, int64(1), unread.MsgCount) 2516 r = th.Client.SetPostUnread(u1.Id, pp1.Id, true) 2517 assert.Equal(t, 200, r.StatusCode) 2518 unread, err = th.App.GetChannelUnread(th.BasicPrivateChannel.Id, u1.Id) 2519 require.Nil(t, err) 2520 assert.Equal(t, int64(2), unread.MsgCount) 2521 }) 2522 2523 t.Run("Can't unread an imaginary post", func(t *testing.T) { 2524 r := th.Client.SetPostUnread(u1.Id, "invalid4ofngungryquinj976y", true) 2525 assert.Equal(t, http.StatusForbidden, r.StatusCode) 2526 }) 2527 2528 // let's create another user to test permissions 2529 u3 := th.CreateUser() 2530 c3 := th.CreateClient() 2531 c3.Login(u3.Email, u3.Password) 2532 2533 t.Run("Can't unread channels you don't belong to", func(t *testing.T) { 2534 r := c3.SetPostUnread(u3.Id, pp1.Id, true) 2535 assert.Equal(t, http.StatusForbidden, r.StatusCode) 2536 }) 2537 2538 t.Run("Can't unread users you don't have permission to edit", func(t *testing.T) { 2539 r := c3.SetPostUnread(u1.Id, pp1.Id, true) 2540 assert.Equal(t, http.StatusForbidden, r.StatusCode) 2541 }) 2542 2543 t.Run("Can't unread if user is not logged in", func(t *testing.T) { 2544 th.Client.Logout() 2545 response := th.Client.SetPostUnread(u1.Id, p2.Id, true) 2546 checkHTTPStatus(t, response, http.StatusUnauthorized, true) 2547 }) 2548 } 2549 2550 func TestSetPostUnreadWithoutCollapsedThreads(t *testing.T) { 2551 os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true") 2552 defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS") 2553 th := Setup(t).InitBasic() 2554 defer th.TearDown() 2555 th.App.UpdateConfig(func(cfg *model.Config) { 2556 *cfg.ServiceSettings.ThreadAutoFollow = true 2557 *cfg.ServiceSettings.CollapsedThreads = model.COLLAPSED_THREADS_DEFAULT_ON 2558 }) 2559 2560 // user2: first root mention @user1 2561 // - user1: hello 2562 // - user2: mention @u1 2563 // - user1: another repoy 2564 // - user2: another mention @u1 2565 // user1: a root post 2566 // user2: Another root mention @u1 2567 user1Mention := " @" + th.BasicUser.Username 2568 rootPost1, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "first root mention" + user1Mention}, th.BasicChannel, false, false) 2569 require.Nil(t, appErr) 2570 _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hello"}, th.BasicChannel, false, false) 2571 require.Nil(t, appErr) 2572 replyPost1, appErr := th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "mention" + user1Mention}, th.BasicChannel, false, false) 2573 require.Nil(t, appErr) 2574 _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "another reply"}, th.BasicChannel, false, false) 2575 require.Nil(t, appErr) 2576 _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "another mention" + user1Mention}, th.BasicChannel, false, false) 2577 require.Nil(t, appErr) 2578 _, appErr = th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "a root post"}, th.BasicChannel, false, false) 2579 require.Nil(t, appErr) 2580 _, appErr = th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "another root mention" + user1Mention}, th.BasicChannel, false, false) 2581 require.Nil(t, appErr) 2582 2583 t.Run("Mark reply post as unread", func(t *testing.T) { 2584 resp := th.Client.SetPostUnread(th.BasicUser.Id, replyPost1.Id, false) 2585 CheckNoError(t, resp) 2586 channelUnread, appErr := th.App.GetChannelUnread(th.BasicChannel.Id, th.BasicUser.Id) 2587 require.Nil(t, appErr) 2588 2589 require.Equal(t, int64(3), channelUnread.MentionCount) 2590 // MentionCountRoot should be zero so that supported clients don't show a mention badge for the channel 2591 require.Equal(t, int64(0), channelUnread.MentionCountRoot) 2592 2593 require.Equal(t, int64(5), channelUnread.MsgCount) 2594 // MentionCountRoot should be zero so that supported clients don't show the channel as unread 2595 require.Equal(t, channelUnread.MsgCountRoot, int64(0)) 2596 2597 threadMembership, err := th.App.GetThreadMembershipForUser(th.BasicUser.Id, rootPost1.Id) 2598 require.Nil(t, err) 2599 thread, err := th.App.GetThreadForUser(th.BasicTeam.Id, threadMembership, false) 2600 require.Nil(t, err) 2601 require.Equal(t, int64(2), thread.UnreadMentions) 2602 require.Equal(t, int64(3), thread.UnreadReplies) 2603 }) 2604 2605 t.Run("Mark root post as unread", func(t *testing.T) { 2606 resp := th.Client.SetPostUnread(th.BasicUser.Id, rootPost1.Id, false) 2607 CheckNoError(t, resp) 2608 channelUnread, appErr := th.App.GetChannelUnread(th.BasicChannel.Id, th.BasicUser.Id) 2609 require.Nil(t, appErr) 2610 2611 require.Equal(t, int64(4), channelUnread.MentionCount) 2612 require.Equal(t, int64(2), channelUnread.MentionCountRoot) 2613 2614 require.Equal(t, int64(7), channelUnread.MsgCount) 2615 require.Equal(t, int64(3), channelUnread.MsgCountRoot) 2616 }) 2617 }