// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package api import ( "encoding/json" "net/http" "net/http/httptest" "net/url" "reflect" "strings" "testing" "time" "github.com/mattermost/platform/model" "github.com/mattermost/platform/utils" ) func TestCreatePost(t *testing.T) { th := Setup().InitBasic() Client := th.BasicClient team := th.BasicTeam team2 := th.CreateTeam(th.BasicClient) user1 := th.BasicUser user3 := th.CreateUser(th.BasicClient) LinkUserToTeam(user3, team2) channel1 := th.BasicChannel channel2 := th.CreateChannel(Client, team) filenames := []string{"/12345678901234567890123456/12345678901234567890123456/12345678901234567890123456/test.png", "/" + channel1.Id + "/" + user1.Id + "/test.png", "www.mattermost.com/fake/url", "junk"} post1 := &model.Post{ChannelId: channel1.Id, Message: "#hashtag a" + model.NewId() + "a", Filenames: filenames} rpost1, err := Client.CreatePost(post1) if err != nil { t.Fatal(err) } if rpost1.Data.(*model.Post).Message != post1.Message { t.Fatal("message didn't match") } if rpost1.Data.(*model.Post).Hashtags != "#hashtag" { t.Fatal("hashtag didn't match") } if len(rpost1.Data.(*model.Post).Filenames) != 2 { t.Fatal("filenames didn't parse correctly") } post2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: rpost1.Data.(*model.Post).Id} rpost2, err := Client.CreatePost(post2) if err != nil { t.Fatal(err) } post3 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: rpost1.Data.(*model.Post).Id, ParentId: rpost2.Data.(*model.Post).Id} _, err = Client.CreatePost(post3) if err != nil { t.Fatal(err) } post4 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: "junk"} _, err = Client.CreatePost(post4) if err.StatusCode != http.StatusBadRequest { t.Fatal("Should have been invalid param") } post5 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: rpost1.Data.(*model.Post).Id, ParentId: "junk"} _, err = Client.CreatePost(post5) if err.StatusCode != http.StatusBadRequest { t.Fatal("Should have been invalid param") } post1c2 := &model.Post{ChannelId: channel2.Id, Message: "a" + model.NewId() + "a"} rpost1c2, err := Client.CreatePost(post1c2) post2c2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: rpost1c2.Data.(*model.Post).Id} _, err = Client.CreatePost(post2c2) if err.StatusCode != http.StatusBadRequest { t.Fatal("Should have been invalid param") } post6 := &model.Post{ChannelId: "junk", Message: "a" + model.NewId() + "a"} _, err = Client.CreatePost(post6) if err.StatusCode != http.StatusForbidden { t.Fatal("Should have been forbidden") } th.LoginBasic2() post7 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} _, err = Client.CreatePost(post7) if err.StatusCode != http.StatusForbidden { t.Fatal("Should have been forbidden") } Client.Login(user3.Email, user3.Password) Client.SetTeamId(team2.Id) channel3 := th.CreateChannel(Client, team2) post8 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} _, err = Client.CreatePost(post8) if err.StatusCode != http.StatusForbidden { t.Fatal("Should have been forbidden") } if _, err = Client.DoApiPost("/channels/"+channel3.Id+"/create", "garbage"); err == nil { t.Fatal("should have been an error") } } func testCreatePostWithOutgoingHook( t *testing.T, hookContentType string, expectedContentType string, ) { th := Setup().InitSystemAdmin() Client := th.SystemAdminClient team := th.SystemAdminTeam user := th.SystemAdminUser channel := th.CreateChannel(Client, team) enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks defer func() { utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks }() utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true var hook *model.OutgoingWebhook var post *model.Post // Create a test server that is the target of the outgoing webhook. It will // validate the webhook body fields and write to the success channel on // success/failure. success := make(chan bool) wait := make(chan bool, 1) ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { <-wait requestContentType := r.Header.Get("Content-Type") if requestContentType != expectedContentType { t.Logf("Content-Type is %s, should be %s", requestContentType, expectedContentType) success <- false return } expectedPayload := &model.OutgoingWebhookPayload{ Token: hook.Token, TeamId: hook.TeamId, TeamDomain: team.Name, ChannelId: post.ChannelId, ChannelName: channel.Name, Timestamp: post.CreateAt, UserId: post.UserId, UserName: user.Username, PostId: post.Id, Text: post.Message, TriggerWord: strings.Fields(post.Message)[0], } // depending on the Content-Type, we expect to find a JSON or form encoded payload if requestContentType == "application/json" { decoder := json.NewDecoder(r.Body) o := &model.OutgoingWebhookPayload{} decoder.Decode(&o) if !reflect.DeepEqual(expectedPayload, o) { t.Logf("JSON payload is %+v, should be %+v", o, expectedPayload) success <- false return } } else { err := r.ParseForm() if err != nil { t.Logf("Error parsing form: %q", err) success <- false return } expectedFormValues, _ := url.ParseQuery(expectedPayload.ToFormValues()) if !reflect.DeepEqual(expectedFormValues, r.Form) { t.Logf("Form values are %q, should be %q", r.Form, expectedFormValues) success <- false return } } success <- true })) defer ts.Close() // create an outgoing webhook, passing it the test server URL triggerWord := "bingo" hook = &model.OutgoingWebhook{ ChannelId: channel.Id, ContentType: hookContentType, TriggerWords: []string{triggerWord}, CallbackURLs: []string{ts.URL}, } if result, err := Client.CreateOutgoingWebhook(hook); err != nil { t.Fatal(err) } else { hook = result.Data.(*model.OutgoingWebhook) } // create a post to trigger the webhook message := triggerWord + " lorem ipusm" post = &model.Post{ ChannelId: channel.Id, Message: message, } if result, err := Client.CreatePost(post); err != nil { t.Fatal(err) } else { post = result.Data.(*model.Post) } wait <- true // We wait for the test server to write to the success channel and we make // the test fail if that doesn't happen before the timeout. select { case ok := <-success: if !ok { t.Fatal("Test server was sent an invalid webhook.") } case <-time.After(time.Second): t.Fatal("Timeout, test server wasn't sent the webhook.") } } func TestCreatePostWithOutgoingHook_form_urlencoded(t *testing.T) { testCreatePostWithOutgoingHook(t, "application/x-www-form-urlencoded", "application/x-www-form-urlencoded") } func TestCreatePostWithOutgoingHook_json(t *testing.T) { testCreatePostWithOutgoingHook(t, "application/json", "application/json") } // hooks created before we added the ContentType field should be considered as // application/x-www-form-urlencoded func TestCreatePostWithOutgoingHook_no_content_type(t *testing.T) { testCreatePostWithOutgoingHook(t, "", "application/x-www-form-urlencoded") } func TestUpdatePost(t *testing.T) { th := Setup().InitBasic() Client := th.BasicClient channel1 := th.BasicChannel post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} rpost1, err := Client.CreatePost(post1) if err != nil { t.Fatal(err) } if rpost1.Data.(*model.Post).Message != post1.Message { t.Fatal("full name didn't match") } post2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: rpost1.Data.(*model.Post).Id} rpost2, err := Client.CreatePost(post2) if err != nil { t.Fatal(err) } msg2 := "a" + model.NewId() + " update post 1" rpost2.Data.(*model.Post).Message = msg2 if rupost2, err := Client.UpdatePost(rpost2.Data.(*model.Post)); err != nil { t.Fatal(err) } else { if rupost2.Data.(*model.Post).Message != msg2 { t.Fatal("failed to updates") } } msg1 := "#hashtag a" + model.NewId() + " update post 2" rpost1.Data.(*model.Post).Message = msg1 if rupost1, err := Client.UpdatePost(rpost1.Data.(*model.Post)); err != nil { t.Fatal(err) } else { if rupost1.Data.(*model.Post).Message != msg1 && rupost1.Data.(*model.Post).Hashtags != "#hashtag" { t.Fatal("failed to updates") } } up12 := &model.Post{Id: rpost1.Data.(*model.Post).Id, ChannelId: channel1.Id, Message: "a" + model.NewId() + " updaet post 1 update 2"} if rup12, err := Client.UpdatePost(up12); err != nil { t.Fatal(err) } else { if rup12.Data.(*model.Post).Message != up12.Message { t.Fatal("failed to updates") } } post3 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", Type: model.POST_JOIN_LEAVE} rpost3, err := Client.CreatePost(post3) if err != nil { t.Fatal(err) } up3 := &model.Post{Id: rpost3.Data.(*model.Post).Id, ChannelId: channel1.Id, Message: "a" + model.NewId() + " update post 3"} if _, err := Client.UpdatePost(up3); err == nil { t.Fatal("shouldn't have been able to update system message") } } func TestGetPosts(t *testing.T) { th := Setup().InitBasic() Client := th.BasicClient channel1 := th.BasicChannel time.Sleep(10 * time.Millisecond) post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post) time.Sleep(10 * time.Millisecond) post1a1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: post1.Id} post1a1 = Client.Must(Client.CreatePost(post1a1)).Data.(*model.Post) time.Sleep(10 * time.Millisecond) post2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post) time.Sleep(10 * time.Millisecond) post3 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} post3 = Client.Must(Client.CreatePost(post3)).Data.(*model.Post) time.Sleep(10 * time.Millisecond) post3a1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: post3.Id} post3a1 = Client.Must(Client.CreatePost(post3a1)).Data.(*model.Post) r1 := Client.Must(Client.GetPosts(channel1.Id, 0, 2, "")).Data.(*model.PostList) if r1.Order[0] != post3a1.Id { t.Fatal("wrong order") } if r1.Order[1] != post3.Id { t.Fatal("wrong order") } if len(r1.Posts) != 2 { // 3a1 and 3; 3a1's parent already there t.Fatal("wrong size") } r2 := Client.Must(Client.GetPosts(channel1.Id, 2, 2, "")).Data.(*model.PostList) if r2.Order[0] != post2.Id { t.Fatal("wrong order") } if r2.Order[1] != post1a1.Id { t.Fatal("wrong order") } if len(r2.Posts) != 3 { // 2 and 1a1; + 1a1's parent t.Log(r2.Posts) t.Fatal("wrong size") } } func TestGetPostsSince(t *testing.T) { th := Setup().InitBasic() Client := th.BasicClient channel1 := th.BasicChannel time.Sleep(10 * time.Millisecond) post0 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} post0 = Client.Must(Client.CreatePost(post0)).Data.(*model.Post) time.Sleep(10 * time.Millisecond) post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post) time.Sleep(10 * time.Millisecond) post1a1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: post1.Id} post1a1 = Client.Must(Client.CreatePost(post1a1)).Data.(*model.Post) time.Sleep(10 * time.Millisecond) post2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post) time.Sleep(10 * time.Millisecond) post3 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} post3 = Client.Must(Client.CreatePost(post3)).Data.(*model.Post) time.Sleep(10 * time.Millisecond) post3a1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: post3.Id} post3a1 = Client.Must(Client.CreatePost(post3a1)).Data.(*model.Post) r1 := Client.Must(Client.GetPostsSince(channel1.Id, post1.CreateAt)).Data.(*model.PostList) if r1.Order[0] != post3a1.Id { t.Fatal("wrong order") } if r1.Order[1] != post3.Id { t.Fatal("wrong order") } if len(r1.Posts) != 5 { t.Fatal("wrong size") } now := model.GetMillis() r2 := Client.Must(Client.GetPostsSince(channel1.Id, now)).Data.(*model.PostList) if len(r2.Posts) != 0 { t.Fatal("should have been empty") } post2.Message = "new message" Client.Must(Client.UpdatePost(post2)) r3 := Client.Must(Client.GetPostsSince(channel1.Id, now)).Data.(*model.PostList) if len(r3.Order) != 2 { // 2 because deleted post is returned as well t.Fatal("missing post update") } } func TestGetPostsBeforeAfter(t *testing.T) { th := Setup().InitBasic() Client := th.BasicClient channel1 := th.BasicChannel time.Sleep(10 * time.Millisecond) post0 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} post0 = Client.Must(Client.CreatePost(post0)).Data.(*model.Post) time.Sleep(10 * time.Millisecond) post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post) time.Sleep(10 * time.Millisecond) post1a1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: post1.Id} post1a1 = Client.Must(Client.CreatePost(post1a1)).Data.(*model.Post) time.Sleep(10 * time.Millisecond) post2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post) time.Sleep(10 * time.Millisecond) post3 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} post3 = Client.Must(Client.CreatePost(post3)).Data.(*model.Post) time.Sleep(10 * time.Millisecond) post3a1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: post3.Id} post3a1 = Client.Must(Client.CreatePost(post3a1)).Data.(*model.Post) r1 := Client.Must(Client.GetPostsBefore(channel1.Id, post1a1.Id, 0, 10, "")).Data.(*model.PostList) if r1.Order[0] != post1.Id { t.Fatal("wrong order") } if r1.Order[1] != post0.Id { t.Fatal("wrong order") } if len(r1.Posts) != 3 { t.Log(r1.Posts) t.Fatal("wrong size") } r2 := Client.Must(Client.GetPostsAfter(channel1.Id, post3a1.Id, 0, 3, "")).Data.(*model.PostList) if len(r2.Posts) != 0 { t.Fatal("should have been empty") } post2.Message = "new message" Client.Must(Client.UpdatePost(post2)) r3 := Client.Must(Client.GetPostsAfter(channel1.Id, post1a1.Id, 0, 2, "")).Data.(*model.PostList) if r3.Order[0] != post3.Id { t.Fatal("wrong order") } if r3.Order[1] != post2.Id { t.Fatal("wrong order") } if len(r3.Order) != 2 { t.Fatal("missing post update") } } func TestSearchPosts(t *testing.T) { th := Setup().InitBasic() Client := th.BasicClient channel1 := th.BasicChannel post1 := &model.Post{ChannelId: channel1.Id, Message: "search for post1"} post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post) post2 := &model.Post{ChannelId: channel1.Id, Message: "search for post2"} post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post) post3 := &model.Post{ChannelId: channel1.Id, Message: "#hashtag search for post3"} post3 = Client.Must(Client.CreatePost(post3)).Data.(*model.Post) post4 := &model.Post{ChannelId: channel1.Id, Message: "hashtag for post4"} post4 = Client.Must(Client.CreatePost(post4)).Data.(*model.Post) r1 := Client.Must(Client.SearchPosts("search", false)).Data.(*model.PostList) if len(r1.Order) != 3 { t.Fatal("wrong search") } r2 := Client.Must(Client.SearchPosts("post2", false)).Data.(*model.PostList) if len(r2.Order) != 1 && r2.Order[0] == post2.Id { t.Fatal("wrong search") } r3 := Client.Must(Client.SearchPosts("#hashtag", false)).Data.(*model.PostList) if len(r3.Order) != 1 && r3.Order[0] == post3.Id { t.Fatal("wrong search") } if r4 := Client.Must(Client.SearchPosts("*", false)).Data.(*model.PostList); len(r4.Order) != 0 { t.Fatal("searching for just * shouldn't return any results") } r5 := Client.Must(Client.SearchPosts("post1 post2", true)).Data.(*model.PostList) if len(r5.Order) != 2 { t.Fatal("wrong search results") } } func TestSearchHashtagPosts(t *testing.T) { th := Setup().InitBasic() Client := th.BasicClient channel1 := th.BasicChannel post1 := &model.Post{ChannelId: channel1.Id, Message: "#sgtitlereview with space"} post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post) post2 := &model.Post{ChannelId: channel1.Id, Message: "#sgtitlereview\n with return"} post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post) post3 := &model.Post{ChannelId: channel1.Id, Message: "no hashtag"} post3 = Client.Must(Client.CreatePost(post3)).Data.(*model.Post) r1 := Client.Must(Client.SearchPosts("#sgtitlereview", false)).Data.(*model.PostList) if len(r1.Order) != 2 { t.Fatal("wrong search") } } func TestSearchPostsInChannel(t *testing.T) { th := Setup().InitBasic() Client := th.BasicClient channel1 := th.BasicChannel team := th.BasicTeam post1 := &model.Post{ChannelId: channel1.Id, Message: "sgtitlereview with space"} post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post) channel2 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel) channel3 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel) post2 := &model.Post{ChannelId: channel2.Id, Message: "sgtitlereview\n with return"} post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post) post3 := &model.Post{ChannelId: channel2.Id, Message: "other message with no return"} post3 = Client.Must(Client.CreatePost(post3)).Data.(*model.Post) post4 := &model.Post{ChannelId: channel3.Id, Message: "other message with no return"} post4 = Client.Must(Client.CreatePost(post4)).Data.(*model.Post) if result := Client.Must(Client.SearchPosts("channel:", false)).Data.(*model.PostList); len(result.Order) != 0 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } if result := Client.Must(Client.SearchPosts("in:", false)).Data.(*model.PostList); len(result.Order) != 0 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } if result := Client.Must(Client.SearchPosts("channel:"+channel1.Name, false)).Data.(*model.PostList); len(result.Order) != 2 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } if result := Client.Must(Client.SearchPosts("in: "+channel2.Name, false)).Data.(*model.PostList); len(result.Order) != 2 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } if result := Client.Must(Client.SearchPosts("channel: "+channel2.Name, false)).Data.(*model.PostList); len(result.Order) != 2 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } if result := Client.Must(Client.SearchPosts("ChAnNeL: "+channel2.Name, false)).Data.(*model.PostList); len(result.Order) != 2 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } if result := Client.Must(Client.SearchPosts("sgtitlereview", false)).Data.(*model.PostList); len(result.Order) != 2 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } if result := Client.Must(Client.SearchPosts("sgtitlereview channel:"+channel1.Name, false)).Data.(*model.PostList); len(result.Order) != 1 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } if result := Client.Must(Client.SearchPosts("sgtitlereview in: "+channel2.Name, false)).Data.(*model.PostList); len(result.Order) != 1 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } if result := Client.Must(Client.SearchPosts("sgtitlereview channel: "+channel2.Name, false)).Data.(*model.PostList); len(result.Order) != 1 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } if result := Client.Must(Client.SearchPosts("channel: "+channel2.Name+" channel: "+channel3.Name, false)).Data.(*model.PostList); len(result.Order) != 3 { t.Fatalf("wrong number of posts returned :) %v :) %v", result.Posts, result.Order) } } func TestSearchPostsFromUser(t *testing.T) { th := Setup().InitBasic() Client := th.BasicClient channel1 := th.BasicChannel team := th.BasicTeam user1 := th.BasicUser user2 := th.BasicUser2 channel2 := th.CreateChannel(Client, team) Client.Must(Client.AddChannelMember(channel1.Id, th.BasicUser2.Id)) Client.Must(Client.AddChannelMember(channel2.Id, th.BasicUser2.Id)) user3 := th.CreateUser(Client) LinkUserToTeam(user3, team) Client.Must(Client.AddChannelMember(channel1.Id, user3.Id)) Client.Must(Client.AddChannelMember(channel2.Id, user3.Id)) post1 := &model.Post{ChannelId: channel1.Id, Message: "sgtitlereview with space"} post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post) th.LoginBasic2() post2 := &model.Post{ChannelId: channel2.Id, Message: "sgtitlereview\n with return"} post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post) if result := Client.Must(Client.SearchPosts("from: "+user1.Username, false)).Data.(*model.PostList); len(result.Order) != 2 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } if result := Client.Must(Client.SearchPosts("from: "+user2.Username, false)).Data.(*model.PostList); len(result.Order) != 1 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } if result := Client.Must(Client.SearchPosts("from: "+user2.Username+" sgtitlereview", false)).Data.(*model.PostList); len(result.Order) != 1 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } post3 := &model.Post{ChannelId: channel1.Id, Message: "hullo"} post3 = Client.Must(Client.CreatePost(post3)).Data.(*model.Post) if result := Client.Must(Client.SearchPosts("from: "+user2.Username+" in:"+channel1.Name, false)).Data.(*model.PostList); len(result.Order) != 1 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } Client.Login(user3.Email, user3.Password) // wait for the join/leave messages to be created for user3 since they're done asynchronously time.Sleep(100 * time.Millisecond) if result := Client.Must(Client.SearchPosts("from: "+user2.Username, false)).Data.(*model.PostList); len(result.Order) != 2 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } if result := Client.Must(Client.SearchPosts("from: "+user2.Username+" from: "+user3.Username, false)).Data.(*model.PostList); len(result.Order) != 2 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } if result := Client.Must(Client.SearchPosts("from: "+user2.Username+" from: "+user3.Username+" in:"+channel2.Name, false)).Data.(*model.PostList); len(result.Order) != 1 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } post4 := &model.Post{ChannelId: channel2.Id, Message: "coconut"} post4 = Client.Must(Client.CreatePost(post4)).Data.(*model.Post) if result := Client.Must(Client.SearchPosts("from: "+user2.Username+" from: "+user3.Username+" in:"+channel2.Name+" coconut", false)).Data.(*model.PostList); len(result.Order) != 1 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } } func TestGetPostsCache(t *testing.T) { th := Setup().InitBasic() Client := th.BasicClient channel1 := th.BasicChannel time.Sleep(10 * time.Millisecond) post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post) time.Sleep(10 * time.Millisecond) post2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post) time.Sleep(10 * time.Millisecond) post3 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} post3 = Client.Must(Client.CreatePost(post3)).Data.(*model.Post) etag := Client.Must(Client.GetPosts(channel1.Id, 0, 2, "")).Etag // test etag caching if cache_result, err := Client.GetPosts(channel1.Id, 0, 2, etag); err != nil { t.Fatal(err) } else if cache_result.Data.(*model.PostList) != nil { t.Log(cache_result.Data) t.Fatal("cache should be empty") } etag = Client.Must(Client.GetPost(channel1.Id, post1.Id, "")).Etag // test etag caching if cache_result, err := Client.GetPost(channel1.Id, post1.Id, etag); err != nil { t.Fatal(err) } else if cache_result.Data.(*model.PostList) != nil { t.Log(cache_result.Data) t.Fatal("cache should be empty") } } func TestDeletePosts(t *testing.T) { th := Setup().InitBasic() Client := th.BasicClient channel1 := th.BasicChannel UpdateUserToTeamAdmin(th.BasicUser2, th.BasicTeam) time.Sleep(10 * time.Millisecond) post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post) time.Sleep(10 * time.Millisecond) post1a1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: post1.Id} post1a1 = Client.Must(Client.CreatePost(post1a1)).Data.(*model.Post) time.Sleep(10 * time.Millisecond) post1a2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: post1.Id, ParentId: post1a1.Id} post1a2 = Client.Must(Client.CreatePost(post1a2)).Data.(*model.Post) time.Sleep(10 * time.Millisecond) post2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post) time.Sleep(10 * time.Millisecond) post3 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} post3 = Client.Must(Client.CreatePost(post3)).Data.(*model.Post) time.Sleep(10 * time.Millisecond) post3a1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: post3.Id} post3a1 = Client.Must(Client.CreatePost(post3a1)).Data.(*model.Post) time.Sleep(10 * time.Millisecond) Client.Must(Client.DeletePost(channel1.Id, post3.Id)) r2 := Client.Must(Client.GetPosts(channel1.Id, 0, 10, "")).Data.(*model.PostList) if len(r2.Posts) != 5 { t.Fatal("should have returned 4 items") } time.Sleep(10 * time.Millisecond) post4 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} post4 = Client.Must(Client.CreatePost(post4)).Data.(*model.Post) th.LoginBasic2() Client.Must(Client.DeletePost(channel1.Id, post4.Id)) } func TestEmailMention(t *testing.T) { th := Setup().InitBasic() Client := th.BasicClient channel1 := th.BasicChannel post1 := &model.Post{ChannelId: channel1.Id, Message: th.BasicUser.Username} post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post) // No easy way to verify the email was sent, but this will at least cause the server to throw errors if the code is broken } func TestFuzzyPosts(t *testing.T) { th := Setup().InitBasic() Client := th.BasicClient channel1 := th.BasicChannel filenames := []string{"junk"} for i := 0; i < len(utils.FUZZY_STRINGS_POSTS); i++ { post := &model.Post{ChannelId: channel1.Id, Message: utils.FUZZY_STRINGS_POSTS[i], Filenames: filenames} _, err := Client.CreatePost(post) if err != nil { t.Fatal(err) } } } func TestMakeDirectChannelVisible(t *testing.T) { th := Setup().InitBasic() Client := th.BasicClient team := th.BasicTeam user1 := th.BasicUser user2 := th.BasicUser2 th.LoginBasic2() preferences := &model.Preferences{ { UserId: user2.Id, Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW, Name: user1.Id, Value: "false", }, } Client.Must(Client.SetPreferences(preferences)) Client.Must(Client.Logout()) th.LoginBasic() th.BasicClient.SetTeamId(team.Id) channel := Client.Must(Client.CreateDirectChannel(user2.Id)).Data.(*model.Channel) makeDirectChannelVisible(team.Id, channel.Id) if result, err := Client.GetPreference(model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW, user2.Id); err != nil { t.Fatal("Errored trying to set direct channel to be visible for user1") } else if pref := result.Data.(*model.Preference); pref.Value != "true" { t.Fatal("Failed to set direct channel to be visible for user1") } } func TestGetMentionKeywords(t *testing.T) { // user with username or custom mentions enabled user1 := &model.User{ Id: model.NewId(), FirstName: "First", Username: "User", NotifyProps: map[string]string{ "mention_keys": "User,@User,MENTION", }, } profiles := map[string]*model.User{user1.Id: user1} members := map[string]string{user1.Id: user1.Id} mentions := getMentionKeywords(profiles, members) if len(mentions) != 3 { t.Fatal("should've returned two mention keywords") } else if ids, ok := mentions["user"]; !ok || ids[0] != user1.Id { t.Fatal("should've returned mention key of user") } else if ids, ok := mentions["@user"]; !ok || ids[0] != user1.Id { t.Fatal("should've returned mention key of @user") } else if ids, ok := mentions["mention"]; !ok || ids[0] != user1.Id { t.Fatal("should've returned mention key of mention") } // user with first name mention enabled user2 := &model.User{ Id: model.NewId(), FirstName: "First", Username: "User", NotifyProps: map[string]string{ "first_name": "true", }, } profiles = map[string]*model.User{user2.Id: user2} members = map[string]string{user2.Id: user2.Id} mentions = getMentionKeywords(profiles, members) if len(mentions) != 1 { t.Fatal("should've returned one mention keyword") } else if ids, ok := mentions["First"]; !ok || ids[0] != user2.Id { t.Fatal("should've returned mention key of First") } // user with @channel/@all mentions enabled user3 := &model.User{ Id: model.NewId(), FirstName: "First", Username: "User", NotifyProps: map[string]string{ "channel": "true", }, } profiles = map[string]*model.User{user3.Id: user3} members = map[string]string{user3.Id: user3.Id} mentions = getMentionKeywords(profiles, members) if len(mentions) != 2 { t.Fatal("should've returned two mention keywords") } else if ids, ok := mentions["@channel"]; !ok || ids[0] != user3.Id { t.Fatal("should've returned mention key of @channel") } else if ids, ok := mentions["@all"]; !ok || ids[0] != user3.Id { t.Fatal("should've returned mention key of @all") } // user with all types of mentions enabled user4 := &model.User{ Id: model.NewId(), FirstName: "First", Username: "User", NotifyProps: map[string]string{ "mention_keys": "User,@User,MENTION", "first_name": "true", "channel": "true", }, } profiles = map[string]*model.User{user4.Id: user4} members = map[string]string{user4.Id: user4.Id} mentions = getMentionKeywords(profiles, members) if len(mentions) != 6 { t.Fatal("should've returned six mention keywords") } else if ids, ok := mentions["user"]; !ok || ids[0] != user4.Id { t.Fatal("should've returned mention key of user") } else if ids, ok := mentions["@user"]; !ok || ids[0] != user4.Id { t.Fatal("should've returned mention key of @user") } else if ids, ok := mentions["mention"]; !ok || ids[0] != user4.Id { t.Fatal("should've returned mention key of mention") } else if ids, ok := mentions["First"]; !ok || ids[0] != user4.Id { t.Fatal("should've returned mention key of First") } else if ids, ok := mentions["@channel"]; !ok || ids[0] != user4.Id { t.Fatal("should've returned mention key of @channel") } else if ids, ok := mentions["@all"]; !ok || ids[0] != user4.Id { t.Fatal("should've returned mention key of @all") } // multiple users profiles = map[string]*model.User{ user1.Id: user1, user2.Id: user2, user3.Id: user3, user4.Id: user4, } members = map[string]string{ user1.Id: user1.Id, user2.Id: user2.Id, user3.Id: user3.Id, user4.Id: user4.Id, } mentions = getMentionKeywords(profiles, members) if len(mentions) != 6 { t.Fatal("should've returned six mention keywords") } else if ids, ok := mentions["user"]; !ok || len(ids) != 2 || (ids[0] != user1.Id && ids[1] != user1.Id) || (ids[0] != user4.Id && ids[1] != user4.Id) { t.Fatal("should've mentioned user1 and user4 with user") } else if ids, ok := mentions["@user"]; !ok || len(ids) != 2 || (ids[0] != user1.Id && ids[1] != user1.Id) || (ids[0] != user4.Id && ids[1] != user4.Id) { t.Fatal("should've mentioned user1 and user4 with @user") } else if ids, ok := mentions["mention"]; !ok || len(ids) != 2 || (ids[0] != user1.Id && ids[1] != user1.Id) || (ids[0] != user4.Id && ids[1] != user4.Id) { t.Fatal("should've mentioned user1 and user4 with mention") } else if ids, ok := mentions["First"]; !ok || len(ids) != 2 || (ids[0] != user2.Id && ids[1] != user2.Id) || (ids[0] != user4.Id && ids[1] != user4.Id) { t.Fatal("should've mentioned user2 and user4 with mention") } else if ids, ok := mentions["@channel"]; !ok || len(ids) != 2 || (ids[0] != user3.Id && ids[1] != user3.Id) || (ids[0] != user4.Id && ids[1] != user4.Id) { t.Fatal("should've mentioned user3 and user4 with @channel") } else if ids, ok := mentions["@all"]; !ok || len(ids) != 2 || (ids[0] != user3.Id && ids[1] != user3.Id) || (ids[0] != user4.Id && ids[1] != user4.Id) { t.Fatal("should've mentioned user3 and user4 with @all") } // a user that's not in the channel profiles = map[string]*model.User{user4.Id: user4} members = map[string]string{} mentions = getMentionKeywords(profiles, members) if len(mentions) != 1 { t.Fatal("should've returned one mention keyword") } else if ids, ok := mentions["@user"]; !ok || len(ids) != 1 || ids[0] != user4.Id { t.Fatal("should've returned mention key of @user") } } func TestGetExplicitMentionsAtHere(t *testing.T) { // test all the boundary cases that we know can break up terms (and those that we know won't) cases := map[string]bool{ "": false, "here": false, "@here": true, " @here ": true, "\t@here\t": true, "\n@here\n": true, // "!@here!": true, // "@@here@": true, // "#@here#": true, // "$@here$": true, // "%@here%": true, // "^@here^": true, // "&@here&": true, // "*@here*": true, "(@here(": true, ")@here)": true, // "-@here-": true, // "_@here_": true, // "=@here=": true, "+@here+": true, "[@here[": true, "{@here{": true, "]@here]": true, "}@here}": true, "\\@here\\": true, // "|@here|": true, ";@here;": true, ":@here:": true, // "'@here'": true, // "\"@here\"": true, ",@here,": true, "<@here<": true, ".@here.": true, ">@here>": true, "/@here/": true, "?@here?": true, // "`@here`": true, // "~@here~": true, } for message, shouldMention := range cases { if _, hereMentioned := getExplicitMentions(message, nil); hereMentioned && !shouldMention { t.Fatalf("shouldn't have mentioned @here with \"%v\"", message) } else if !hereMentioned && shouldMention { t.Fatalf("should've have mentioned @here with \"%v\"", message) } } // mentioning @here and someone id := model.NewId() if mentions, hereMentioned := getExplicitMentions("@here @user", map[string][]string{"@user": {id}}); !hereMentioned { t.Fatal("should've mentioned @here with \"@here @user\"") } else if len(mentions) != 1 || !mentions[id] { t.Fatal("should've mentioned @user with \"@here @user\"") } } func TestGetExplicitMentions(t *testing.T) { id1 := model.NewId() id2 := model.NewId() // not mentioning anybody message := "this is a message" keywords := map[string][]string{} if mentions, _ := getExplicitMentions(message, keywords); len(mentions) != 0 { t.Fatal("shouldn't have mentioned anybody") } // mentioning a user that doesn't exist message = "this is a message for @user" if mentions, _ := getExplicitMentions(message, keywords); len(mentions) != 0 { t.Fatal("shouldn't have mentioned user that doesn't exist") } // mentioning one person keywords = map[string][]string{"@user": {id1}} if mentions, _ := getExplicitMentions(message, keywords); len(mentions) != 1 || !mentions[id1] { t.Fatal("should've mentioned @user") } // mentioning one person without an @mention message = "this is a message for @user" keywords = map[string][]string{"this": {id1}} if mentions, _ := getExplicitMentions(message, keywords); len(mentions) != 1 || !mentions[id1] { t.Fatal("should've mentioned this") } // mentioning multiple people with one word message = "this is a message for @user" keywords = map[string][]string{"@user": {id1, id2}} if mentions, _ := getExplicitMentions(message, keywords); len(mentions) != 2 || !mentions[id1] || !mentions[id2] { t.Fatal("should've mentioned two users with @user") } // mentioning only one of multiple people keywords = map[string][]string{"@user": {id1}, "@mention": {id2}} if mentions, _ := getExplicitMentions(message, keywords); len(mentions) != 1 || !mentions[id1] || mentions[id2] { t.Fatal("should've mentioned @user and not @mention") } // mentioning multiple people with multiple words message = "this is an @mention for @user" keywords = map[string][]string{"@user": {id1}, "@mention": {id2}} if mentions, _ := getExplicitMentions(message, keywords); len(mentions) != 2 || !mentions[id1] || !mentions[id2] { t.Fatal("should've mentioned two users with @user and @mention") } // mentioning @channel (not a special case, but it's good to double check) message = "this is an message for @channel" keywords = map[string][]string{"@channel": {id1, id2}} if mentions, _ := getExplicitMentions(message, keywords); len(mentions) != 2 || !mentions[id1] || !mentions[id2] { t.Fatal("should've mentioned two users with @channel") } // mentioning @all (not a special case, but it's good to double check) message = "this is an message for @all" keywords = map[string][]string{"@all": {id1, id2}} if mentions, _ := getExplicitMentions(message, keywords); len(mentions) != 2 || !mentions[id1] || !mentions[id2] { t.Fatal("should've mentioned two users with @all") } // mentioning user.period without mentioning user (PLT-3222) message = "user.period doesn't complicate things at all by including periods in their username" keywords = map[string][]string{"user.period": {id1}, "user": {id2}} if mentions, _ := getExplicitMentions(message, keywords); len(mentions) != 1 || !mentions[id1] || mentions[id2] { t.Fatal("should've mentioned user.period and not user") } } func TestGetFlaggedPosts(t *testing.T) { th := Setup().InitBasic() Client := th.BasicClient user1 := th.BasicUser post1 := th.BasicPost preferences := &model.Preferences{ { UserId: user1.Id, Category: model.PREFERENCE_CATEGORY_FLAGGED_POST, Name: post1.Id, Value: "true", }, } Client.Must(Client.SetPreferences(preferences)) r1 := Client.Must(Client.GetFlaggedPosts(0, 2)).Data.(*model.PostList) if len(r1.Order) == 0 { t.Fatal("should have gotten a flagged post") } if _, ok := r1.Posts[post1.Id]; !ok { t.Fatal("missing flagged post") } Client.DeletePreferences(preferences) r2 := Client.Must(Client.GetFlaggedPosts(0, 2)).Data.(*model.PostList) if len(r2.Order) != 0 { t.Fatal("should not have gotten a flagged post") } } func TestGetMessageForNotification(t *testing.T) { Setup() translateFunc := utils.GetUserTranslations("en") post := &model.Post{ Message: "test", Filenames: model.StringArray{}, } if getMessageForNotification(post, translateFunc) != "test" { t.Fatal("should've returned message text") } post.Filenames = model.StringArray{"test1.png"} if getMessageForNotification(post, translateFunc) != "test" { t.Fatal("should've returned message text, even with attachments") } post.Message = "" if message := getMessageForNotification(post, translateFunc); message != "1 image sent: test1.png" { t.Fatal("should've returned number of images:", message) } post.Filenames = model.StringArray{"test1.png", "test2.jpg"} if message := getMessageForNotification(post, translateFunc); message != "2 images sent: test1.png, test2.jpg" { t.Fatal("should've returned number of images:", message) } post.Filenames = model.StringArray{"test1.go"} if message := getMessageForNotification(post, translateFunc); message != "1 file sent: test1.go" { t.Fatal("should've returned number of files:", message) } post.Filenames = model.StringArray{"test1.go", "test2.jpg"} if message := getMessageForNotification(post, translateFunc); message != "2 files sent: test1.go, test2.jpg" { t.Fatal("should've returned number of mixed files:", message) } }