github.com/coincircle/mattermost-server@v4.8.1-0.20180321182714-9d701c704416+incompatible/app/post_test.go (about) 1 // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package app 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "net/http" 10 "net/http/httptest" 11 "strings" 12 "testing" 13 "time" 14 15 "github.com/dyatlov/go-opengraph/opengraph" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/require" 18 19 "github.com/mattermost/mattermost-server/model" 20 ) 21 22 func TestUpdatePostEditAt(t *testing.T) { 23 th := Setup().InitBasic() 24 defer th.TearDown() 25 26 post := &model.Post{} 27 *post = *th.BasicPost 28 29 post.IsPinned = true 30 if saved, err := th.App.UpdatePost(post, true); err != nil { 31 t.Fatal(err) 32 } else if saved.EditAt != post.EditAt { 33 t.Fatal("shouldn't have updated post.EditAt when pinning post") 34 35 *post = *saved 36 } 37 38 time.Sleep(time.Millisecond * 100) 39 40 post.Message = model.NewId() 41 if saved, err := th.App.UpdatePost(post, true); err != nil { 42 t.Fatal(err) 43 } else if saved.EditAt == post.EditAt { 44 t.Fatal("should have updated post.EditAt when updating post message") 45 } 46 } 47 48 func TestPostReplyToPostWhereRootPosterLeftChannel(t *testing.T) { 49 // This test ensures that when replying to a root post made by a user who has since left the channel, the reply 50 // post completes successfully. This is a regression test for PLT-6523. 51 th := Setup().InitBasic() 52 defer th.TearDown() 53 54 channel := th.BasicChannel 55 userInChannel := th.BasicUser2 56 userNotInChannel := th.BasicUser 57 rootPost := th.BasicPost 58 59 if _, err := th.App.AddUserToChannel(userInChannel, channel); err != nil { 60 t.Fatal(err) 61 } 62 63 if err := th.App.RemoveUserFromChannel(userNotInChannel.Id, "", channel); err != nil { 64 t.Fatal(err) 65 } 66 67 replyPost := model.Post{ 68 Message: "asd", 69 ChannelId: channel.Id, 70 RootId: rootPost.Id, 71 ParentId: rootPost.Id, 72 PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()), 73 UserId: userInChannel.Id, 74 CreateAt: 0, 75 } 76 77 if _, err := th.App.CreatePostAsUser(&replyPost); err != nil { 78 t.Fatal(err) 79 } 80 } 81 82 func TestPostAction(t *testing.T) { 83 th := Setup().InitBasic() 84 defer th.TearDown() 85 86 th.App.UpdateConfig(func(cfg *model.Config) { 87 *cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost 127.0.0.1" 88 }) 89 90 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 91 var request model.PostActionIntegrationRequest 92 err := json.NewDecoder(r.Body).Decode(&request) 93 assert.NoError(t, err) 94 assert.Equal(t, request.UserId, th.BasicUser.Id) 95 assert.Equal(t, "foo", request.Context["s"]) 96 assert.EqualValues(t, 3, request.Context["n"]) 97 fmt.Fprintf(w, `{"update": {"message": "updated"}, "ephemeral_text": "foo"}`) 98 })) 99 defer ts.Close() 100 101 interactivePost := model.Post{ 102 Message: "Interactive post", 103 ChannelId: th.BasicChannel.Id, 104 PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()), 105 UserId: th.BasicUser.Id, 106 Props: model.StringInterface{ 107 "attachments": []*model.SlackAttachment{ 108 { 109 Text: "hello", 110 Actions: []*model.PostAction{ 111 { 112 Integration: &model.PostActionIntegration{ 113 Context: model.StringInterface{ 114 "s": "foo", 115 "n": 3, 116 }, 117 URL: ts.URL, 118 }, 119 Name: "action", 120 }, 121 }, 122 }, 123 }, 124 }, 125 } 126 127 post, err := th.App.CreatePostAsUser(&interactivePost) 128 require.Nil(t, err) 129 130 attachments, ok := post.Props["attachments"].([]*model.SlackAttachment) 131 require.True(t, ok) 132 133 require.NotEmpty(t, attachments[0].Actions) 134 require.NotEmpty(t, attachments[0].Actions[0].Id) 135 136 err = th.App.DoPostAction(post.Id, "notavalidid", th.BasicUser.Id) 137 require.NotNil(t, err) 138 assert.Equal(t, http.StatusNotFound, err.StatusCode) 139 140 err = th.App.DoPostAction(post.Id, attachments[0].Actions[0].Id, th.BasicUser.Id) 141 require.Nil(t, err) 142 } 143 144 func TestPostChannelMentions(t *testing.T) { 145 th := Setup().InitBasic() 146 defer th.TearDown() 147 148 channel := th.BasicChannel 149 user := th.BasicUser 150 151 channelToMention, err := th.App.CreateChannel(&model.Channel{ 152 DisplayName: "Mention Test", 153 Name: "mention-test", 154 Type: model.CHANNEL_OPEN, 155 TeamId: th.BasicTeam.Id, 156 }, false) 157 if err != nil { 158 t.Fatal(err.Error()) 159 } 160 defer th.App.PermanentDeleteChannel(channelToMention) 161 162 _, err = th.App.AddUserToChannel(user, channel) 163 require.Nil(t, err) 164 165 post := &model.Post{ 166 Message: fmt.Sprintf("hello, ~%v!", channelToMention.Name), 167 ChannelId: channel.Id, 168 PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()), 169 UserId: user.Id, 170 CreateAt: 0, 171 } 172 173 result, err := th.App.CreatePostAsUser(post) 174 require.Nil(t, err) 175 assert.Equal(t, map[string]interface{}{ 176 "mention-test": map[string]interface{}{ 177 "display_name": "Mention Test", 178 }, 179 }, result.Props["channel_mentions"]) 180 181 post.Message = fmt.Sprintf("goodbye, ~%v!", channelToMention.Name) 182 result, err = th.App.UpdatePost(post, false) 183 require.Nil(t, err) 184 assert.Equal(t, map[string]interface{}{ 185 "mention-test": map[string]interface{}{ 186 "display_name": "Mention Test", 187 }, 188 }, result.Props["channel_mentions"]) 189 } 190 191 func TestImageProxy(t *testing.T) { 192 th := Setup().InitBasic() 193 defer th.TearDown() 194 195 th.App.UpdateConfig(func(cfg *model.Config) { 196 *cfg.ServiceSettings.SiteURL = "http://mymattermost.com" 197 }) 198 199 for name, tc := range map[string]struct { 200 ProxyType string 201 ProxyURL string 202 ProxyOptions string 203 ImageURL string 204 ProxiedImageURL string 205 }{ 206 "atmos/camo": { 207 ProxyType: "atmos/camo", 208 ProxyURL: "https://127.0.0.1", 209 ProxyOptions: "foo", 210 ImageURL: "http://mydomain.com/myimage", 211 ProxiedImageURL: "https://127.0.0.1/f8dace906d23689e8d5b12c3cefbedbf7b9b72f5/687474703a2f2f6d79646f6d61696e2e636f6d2f6d79696d616765", 212 }, 213 "atmos/camo_SameSite": { 214 ProxyType: "atmos/camo", 215 ProxyURL: "https://127.0.0.1", 216 ProxyOptions: "foo", 217 ImageURL: "http://mymattermost.com/myimage", 218 ProxiedImageURL: "http://mymattermost.com/myimage", 219 }, 220 "atmos/camo_PathOnly": { 221 ProxyType: "atmos/camo", 222 ProxyURL: "https://127.0.0.1", 223 ProxyOptions: "foo", 224 ImageURL: "/myimage", 225 ProxiedImageURL: "/myimage", 226 }, 227 "atmos/camo_EmptyImageURL": { 228 ProxyType: "atmos/camo", 229 ProxyURL: "https://127.0.0.1", 230 ProxyOptions: "foo", 231 ImageURL: "", 232 ProxiedImageURL: "", 233 }, 234 } { 235 t.Run(name, func(t *testing.T) { 236 th.App.UpdateConfig(func(cfg *model.Config) { 237 cfg.ServiceSettings.ImageProxyType = model.NewString(tc.ProxyType) 238 cfg.ServiceSettings.ImageProxyOptions = model.NewString(tc.ProxyOptions) 239 cfg.ServiceSettings.ImageProxyURL = model.NewString(tc.ProxyURL) 240 }) 241 242 post := &model.Post{ 243 Id: model.NewId(), 244 Message: "![foo](" + tc.ImageURL + ")", 245 } 246 247 list := model.NewPostList() 248 list.Posts[post.Id] = post 249 250 assert.Equal(t, "![foo]("+tc.ProxiedImageURL+")", th.App.PostListWithProxyAddedToImageURLs(list).Posts[post.Id].Message) 251 assert.Equal(t, "![foo]("+tc.ProxiedImageURL+")", th.App.PostWithProxyAddedToImageURLs(post).Message) 252 253 assert.Equal(t, "![foo]("+tc.ImageURL+")", th.App.PostWithProxyRemovedFromImageURLs(post).Message) 254 post.Message = "![foo](" + tc.ProxiedImageURL + ")" 255 assert.Equal(t, "![foo]("+tc.ImageURL+")", th.App.PostWithProxyRemovedFromImageURLs(post).Message) 256 }) 257 } 258 } 259 260 func TestMakeOpenGraphURLsAbsolute(t *testing.T) { 261 for name, tc := range map[string]struct { 262 HTML string 263 RequestURL string 264 URL string 265 ImageURL string 266 }{ 267 "absolute URLs": { 268 HTML: ` 269 <html> 270 <head> 271 <meta property="og:url" content="https://example.com/apps/mattermost"> 272 <meta property="og:image" content="https://images.example.com/image.png"> 273 </head> 274 </html>`, 275 RequestURL: "https://example.com", 276 URL: "https://example.com/apps/mattermost", 277 ImageURL: "https://images.example.com/image.png", 278 }, 279 "URLs starting with /": { 280 HTML: ` 281 <html> 282 <head> 283 <meta property="og:url" content="/apps/mattermost"> 284 <meta property="og:image" content="/image.png"> 285 </head> 286 </html>`, 287 RequestURL: "http://example.com", 288 URL: "http://example.com/apps/mattermost", 289 ImageURL: "http://example.com/image.png", 290 }, 291 "HTTPS URLs starting with /": { 292 HTML: ` 293 <html> 294 <head> 295 <meta property="og:url" content="/apps/mattermost"> 296 <meta property="og:image" content="/image.png"> 297 </head> 298 </html>`, 299 RequestURL: "https://example.com", 300 URL: "https://example.com/apps/mattermost", 301 ImageURL: "https://example.com/image.png", 302 }, 303 "missing image URL": { 304 HTML: ` 305 <html> 306 <head> 307 <meta property="og:url" content="/apps/mattermost"> 308 </head> 309 </html>`, 310 RequestURL: "http://example.com", 311 URL: "http://example.com/apps/mattermost", 312 ImageURL: "", 313 }, 314 "relative URLs": { 315 HTML: ` 316 <html> 317 <head> 318 <meta property="og:url" content="index.html"> 319 <meta property="og:image" content="../resources/image.png"> 320 </head> 321 </html>`, 322 RequestURL: "http://example.com/content/index.html", 323 URL: "http://example.com/content/index.html", 324 ImageURL: "http://example.com/resources/image.png", 325 }, 326 } { 327 t.Run(name, func(t *testing.T) { 328 og := opengraph.NewOpenGraph() 329 if err := og.ProcessHTML(strings.NewReader(tc.HTML)); err != nil { 330 t.Fatal(err) 331 } 332 333 makeOpenGraphURLsAbsolute(og, tc.RequestURL) 334 335 if og.URL != tc.URL { 336 t.Fatalf("incorrect url, expected %v, got %v", tc.URL, og.URL) 337 } 338 339 if len(og.Images) > 0 { 340 if og.Images[0].URL != tc.ImageURL { 341 t.Fatalf("incorrect image url, expected %v, got %v", tc.ImageURL, og.Images[0].URL) 342 } 343 } else if tc.ImageURL != "" { 344 t.Fatalf("missing image url, expected %v, got nothing", tc.ImageURL) 345 } 346 }) 347 } 348 }