github.com/gigforks/mattermost-server@v4.9.1-0.20180619094218-800d97fa55d0+incompatible/app/webhook_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 "strings" 8 "testing" 9 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 13 "github.com/mattermost/mattermost-server/model" 14 ) 15 16 func TestCreateIncomingWebhookForChannel(t *testing.T) { 17 th := Setup().InitBasic() 18 defer th.TearDown() 19 20 type TestCase struct { 21 EnableIncomingHooks bool 22 EnablePostUsernameOverride bool 23 EnablePostIconOverride bool 24 IncomingWebhook model.IncomingWebhook 25 26 ExpectedError bool 27 ExpectedIncomingWebhook *model.IncomingWebhook 28 } 29 30 for name, tc := range map[string]TestCase{ 31 "webhooks not enabled": { 32 EnableIncomingHooks: false, 33 EnablePostUsernameOverride: false, 34 EnablePostIconOverride: false, 35 IncomingWebhook: model.IncomingWebhook{ 36 DisplayName: "title", 37 Description: "description", 38 ChannelId: th.BasicChannel.Id, 39 }, 40 41 ExpectedError: true, 42 ExpectedIncomingWebhook: nil, 43 }, 44 "valid: username and post icon url ignored, since override not enabled": { 45 EnableIncomingHooks: true, 46 EnablePostUsernameOverride: false, 47 EnablePostIconOverride: false, 48 IncomingWebhook: model.IncomingWebhook{ 49 DisplayName: "title", 50 Description: "description", 51 ChannelId: th.BasicChannel.Id, 52 Username: ":invalid and ignored:", 53 IconURL: "ignored", 54 }, 55 56 ExpectedError: false, 57 ExpectedIncomingWebhook: &model.IncomingWebhook{ 58 DisplayName: "title", 59 Description: "description", 60 ChannelId: th.BasicChannel.Id, 61 }, 62 }, 63 "invalid username, override enabled": { 64 EnableIncomingHooks: true, 65 EnablePostUsernameOverride: true, 66 EnablePostIconOverride: false, 67 IncomingWebhook: model.IncomingWebhook{ 68 DisplayName: "title", 69 Description: "description", 70 ChannelId: th.BasicChannel.Id, 71 Username: ":invalid:", 72 }, 73 74 ExpectedError: true, 75 ExpectedIncomingWebhook: nil, 76 }, 77 "valid, no username or post icon url provided": { 78 EnableIncomingHooks: true, 79 EnablePostUsernameOverride: true, 80 EnablePostIconOverride: true, 81 IncomingWebhook: model.IncomingWebhook{ 82 DisplayName: "title", 83 Description: "description", 84 ChannelId: th.BasicChannel.Id, 85 }, 86 87 ExpectedError: false, 88 ExpectedIncomingWebhook: &model.IncomingWebhook{ 89 DisplayName: "title", 90 Description: "description", 91 ChannelId: th.BasicChannel.Id, 92 }, 93 }, 94 "valid, with username and post icon": { 95 EnableIncomingHooks: true, 96 EnablePostUsernameOverride: true, 97 EnablePostIconOverride: true, 98 IncomingWebhook: model.IncomingWebhook{ 99 DisplayName: "title", 100 Description: "description", 101 ChannelId: th.BasicChannel.Id, 102 Username: "valid", 103 IconURL: "http://example.com/icon", 104 }, 105 106 ExpectedError: false, 107 ExpectedIncomingWebhook: &model.IncomingWebhook{ 108 DisplayName: "title", 109 Description: "description", 110 ChannelId: th.BasicChannel.Id, 111 Username: "valid", 112 IconURL: "http://example.com/icon", 113 }, 114 }, 115 } { 116 t.Run(name, func(t *testing.T) { 117 assert := assert.New(t) 118 119 th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = tc.EnableIncomingHooks }) 120 th.App.UpdateConfig(func(cfg *model.Config) { 121 cfg.ServiceSettings.EnablePostUsernameOverride = tc.EnablePostUsernameOverride 122 }) 123 th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnablePostIconOverride = tc.EnablePostIconOverride }) 124 125 createdHook, err := th.App.CreateIncomingWebhookForChannel(th.BasicUser.Id, th.BasicChannel, &tc.IncomingWebhook) 126 if tc.ExpectedError && err == nil { 127 t.Fatal("should have failed") 128 } else if !tc.ExpectedError && err != nil { 129 t.Fatalf("should not have failed: %v", err.Error()) 130 } 131 if createdHook != nil { 132 defer th.App.DeleteIncomingWebhook(createdHook.Id) 133 } 134 if tc.ExpectedIncomingWebhook == nil { 135 assert.Nil(createdHook, "expected nil webhook") 136 } else if assert.NotNil(createdHook, "expected non-nil webhook") { 137 assert.Equal(tc.ExpectedIncomingWebhook.DisplayName, createdHook.DisplayName) 138 assert.Equal(tc.ExpectedIncomingWebhook.Description, createdHook.Description) 139 assert.Equal(tc.ExpectedIncomingWebhook.ChannelId, createdHook.ChannelId) 140 assert.Equal(tc.ExpectedIncomingWebhook.Username, createdHook.Username) 141 assert.Equal(tc.ExpectedIncomingWebhook.IconURL, createdHook.IconURL) 142 } 143 }) 144 } 145 } 146 147 func TestUpdateIncomingWebhook(t *testing.T) { 148 th := Setup().InitBasic() 149 defer th.TearDown() 150 151 type TestCase struct { 152 EnableIncomingHooks bool 153 EnablePostUsernameOverride bool 154 EnablePostIconOverride bool 155 IncomingWebhook model.IncomingWebhook 156 157 ExpectedError bool 158 ExpectedIncomingWebhook *model.IncomingWebhook 159 } 160 161 for name, tc := range map[string]TestCase{ 162 "webhooks not enabled": { 163 EnableIncomingHooks: false, 164 EnablePostUsernameOverride: false, 165 EnablePostIconOverride: false, 166 IncomingWebhook: model.IncomingWebhook{ 167 DisplayName: "title", 168 Description: "description", 169 ChannelId: th.BasicChannel.Id, 170 }, 171 172 ExpectedError: true, 173 ExpectedIncomingWebhook: nil, 174 }, 175 "valid: username and post icon url ignored, since override not enabled": { 176 EnableIncomingHooks: true, 177 EnablePostUsernameOverride: false, 178 EnablePostIconOverride: false, 179 IncomingWebhook: model.IncomingWebhook{ 180 DisplayName: "title", 181 Description: "description", 182 ChannelId: th.BasicChannel.Id, 183 Username: ":invalid and ignored:", 184 IconURL: "ignored", 185 }, 186 187 ExpectedError: false, 188 ExpectedIncomingWebhook: &model.IncomingWebhook{ 189 DisplayName: "title", 190 Description: "description", 191 ChannelId: th.BasicChannel.Id, 192 }, 193 }, 194 "invalid username, override enabled": { 195 EnableIncomingHooks: true, 196 EnablePostUsernameOverride: true, 197 EnablePostIconOverride: false, 198 IncomingWebhook: model.IncomingWebhook{ 199 DisplayName: "title", 200 Description: "description", 201 ChannelId: th.BasicChannel.Id, 202 Username: ":invalid:", 203 }, 204 205 ExpectedError: true, 206 ExpectedIncomingWebhook: nil, 207 }, 208 "valid, no username or post icon url provided": { 209 EnableIncomingHooks: true, 210 EnablePostUsernameOverride: true, 211 EnablePostIconOverride: true, 212 IncomingWebhook: model.IncomingWebhook{ 213 DisplayName: "title", 214 Description: "description", 215 ChannelId: th.BasicChannel.Id, 216 }, 217 218 ExpectedError: false, 219 ExpectedIncomingWebhook: &model.IncomingWebhook{ 220 DisplayName: "title", 221 Description: "description", 222 ChannelId: th.BasicChannel.Id, 223 }, 224 }, 225 "valid, with username and post icon": { 226 EnableIncomingHooks: true, 227 EnablePostUsernameOverride: true, 228 EnablePostIconOverride: true, 229 IncomingWebhook: model.IncomingWebhook{ 230 DisplayName: "title", 231 Description: "description", 232 ChannelId: th.BasicChannel.Id, 233 Username: "valid", 234 IconURL: "http://example.com/icon", 235 }, 236 237 ExpectedError: false, 238 ExpectedIncomingWebhook: &model.IncomingWebhook{ 239 DisplayName: "title", 240 Description: "description", 241 ChannelId: th.BasicChannel.Id, 242 Username: "valid", 243 IconURL: "http://example.com/icon", 244 }, 245 }, 246 } { 247 t.Run(name, func(t *testing.T) { 248 assert := assert.New(t) 249 250 th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) 251 252 hook, err := th.App.CreateIncomingWebhookForChannel(th.BasicUser.Id, th.BasicChannel, &model.IncomingWebhook{ 253 ChannelId: th.BasicChannel.Id, 254 }) 255 if err != nil { 256 t.Fatal(err.Error()) 257 } 258 defer th.App.DeleteIncomingWebhook(hook.Id) 259 260 th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = tc.EnableIncomingHooks }) 261 th.App.UpdateConfig(func(cfg *model.Config) { 262 cfg.ServiceSettings.EnablePostUsernameOverride = tc.EnablePostUsernameOverride 263 }) 264 th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnablePostIconOverride = tc.EnablePostIconOverride }) 265 266 updatedHook, err := th.App.UpdateIncomingWebhook(hook, &tc.IncomingWebhook) 267 if tc.ExpectedError && err == nil { 268 t.Fatal("should have failed") 269 } else if !tc.ExpectedError && err != nil { 270 t.Fatalf("should not have failed: %v", err.Error()) 271 } 272 if tc.ExpectedIncomingWebhook == nil { 273 assert.Nil(updatedHook, "expected nil webhook") 274 } else if assert.NotNil(updatedHook, "expected non-nil webhook") { 275 assert.Equal(tc.ExpectedIncomingWebhook.DisplayName, updatedHook.DisplayName) 276 assert.Equal(tc.ExpectedIncomingWebhook.Description, updatedHook.Description) 277 assert.Equal(tc.ExpectedIncomingWebhook.ChannelId, updatedHook.ChannelId) 278 assert.Equal(tc.ExpectedIncomingWebhook.Username, updatedHook.Username) 279 assert.Equal(tc.ExpectedIncomingWebhook.IconURL, updatedHook.IconURL) 280 } 281 }) 282 } 283 } 284 285 func TestCreateWebhookPost(t *testing.T) { 286 th := Setup().InitBasic() 287 defer th.TearDown() 288 289 th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) 290 291 hook, err := th.App.CreateIncomingWebhookForChannel(th.BasicUser.Id, th.BasicChannel, &model.IncomingWebhook{ChannelId: th.BasicChannel.Id}) 292 if err != nil { 293 t.Fatal(err.Error()) 294 } 295 defer th.App.DeleteIncomingWebhook(hook.Id) 296 297 post, err := th.App.CreateWebhookPost(hook.UserId, th.BasicChannel, "foo", "user", "http://iconurl", model.StringInterface{ 298 "attachments": []*model.SlackAttachment{ 299 { 300 Text: "text", 301 }, 302 }, 303 "webhook_display_name": hook.DisplayName, 304 }, model.POST_SLACK_ATTACHMENT, "") 305 if err != nil { 306 t.Fatal(err.Error()) 307 } 308 309 for _, k := range []string{"from_webhook", "attachments", "webhook_display_name"} { 310 if _, ok := post.Props[k]; !ok { 311 t.Log("missing one props: " + k) 312 t.Fatal(k) 313 } 314 } 315 316 _, err = th.App.CreateWebhookPost(hook.UserId, th.BasicChannel, "foo", "user", "http://iconurl", nil, model.POST_SYSTEM_GENERIC, "") 317 if err == nil { 318 t.Fatal("should have failed - bad post type") 319 } 320 321 expectedText := "`<>|<>|`" 322 post, err = th.App.CreateWebhookPost(hook.UserId, th.BasicChannel, expectedText, "user", "http://iconurl", model.StringInterface{ 323 "attachments": []*model.SlackAttachment{ 324 { 325 Text: "text", 326 }, 327 }, 328 "webhook_display_name": hook.DisplayName, 329 }, model.POST_SLACK_ATTACHMENT, "") 330 if err != nil { 331 t.Fatal(err.Error()) 332 } 333 assert.Equal(t, expectedText, post.Message) 334 335 expectedText = "< | \n|\n>" 336 post, err = th.App.CreateWebhookPost(hook.UserId, th.BasicChannel, expectedText, "user", "http://iconurl", model.StringInterface{ 337 "attachments": []*model.SlackAttachment{ 338 { 339 Text: "text", 340 }, 341 }, 342 "webhook_display_name": hook.DisplayName, 343 }, model.POST_SLACK_ATTACHMENT, "") 344 if err != nil { 345 t.Fatal(err.Error()) 346 } 347 assert.Equal(t, expectedText, post.Message) 348 349 expectedText = `commit bc95839e4a430ace453e8b209a3723c000c1729a 350 Author: foo <foo@example.org> 351 Date: Thu Mar 1 19:46:54 2018 +0300 352 353 commit message 2 354 355 test | 1 + 356 1 file changed, 1 insertion(+) 357 358 commit 5df78b7139b543997838071cd912e375d8bd69b2 359 Author: foo <foo@example.org> 360 Date: Thu Mar 1 19:46:48 2018 +0300 361 362 commit message 1 363 364 test | 3 +++ 365 1 file changed, 3 insertions(+)` 366 post, err = th.App.CreateWebhookPost(hook.UserId, th.BasicChannel, expectedText, "user", "http://iconurl", model.StringInterface{ 367 "attachments": []*model.SlackAttachment{ 368 { 369 Text: "text", 370 }, 371 }, 372 "webhook_display_name": hook.DisplayName, 373 }, model.POST_SLACK_ATTACHMENT, "") 374 if err != nil { 375 t.Fatal(err.Error()) 376 } 377 assert.Equal(t, expectedText, post.Message) 378 } 379 380 func TestSplitWebhookPost(t *testing.T) { 381 type TestCase struct { 382 Post *model.Post 383 Expected []*model.Post 384 } 385 386 maxPostSize := 10000 387 388 for name, tc := range map[string]TestCase{ 389 "LongPost": { 390 Post: &model.Post{ 391 Message: strings.Repeat("本", maxPostSize*3/2), 392 }, 393 Expected: []*model.Post{ 394 { 395 Message: strings.Repeat("本", maxPostSize), 396 }, 397 { 398 Message: strings.Repeat("本", maxPostSize/2), 399 }, 400 }, 401 }, 402 "LongPostAndMultipleAttachments": { 403 Post: &model.Post{ 404 Message: strings.Repeat("本", maxPostSize*3/2), 405 Props: map[string]interface{}{ 406 "attachments": []*model.SlackAttachment{ 407 &model.SlackAttachment{ 408 Text: strings.Repeat("本", 1000), 409 }, 410 &model.SlackAttachment{ 411 Text: strings.Repeat("本", 2000), 412 }, 413 &model.SlackAttachment{ 414 Text: strings.Repeat("本", model.POST_PROPS_MAX_USER_RUNES-1000), 415 }, 416 }, 417 }, 418 }, 419 Expected: []*model.Post{ 420 { 421 Message: strings.Repeat("本", maxPostSize), 422 }, 423 { 424 Message: strings.Repeat("本", maxPostSize/2), 425 Props: map[string]interface{}{ 426 "attachments": []*model.SlackAttachment{ 427 &model.SlackAttachment{ 428 Text: strings.Repeat("本", 1000), 429 }, 430 &model.SlackAttachment{ 431 Text: strings.Repeat("本", 2000), 432 }, 433 }, 434 }, 435 }, 436 { 437 Props: map[string]interface{}{ 438 "attachments": []*model.SlackAttachment{ 439 &model.SlackAttachment{ 440 Text: strings.Repeat("本", model.POST_PROPS_MAX_USER_RUNES-1000), 441 }, 442 }, 443 }, 444 }, 445 }, 446 }, 447 "UnsplittableProps": { 448 Post: &model.Post{ 449 Message: "foo", 450 Props: map[string]interface{}{ 451 "foo": strings.Repeat("x", model.POST_PROPS_MAX_USER_RUNES*2), 452 }, 453 }, 454 }, 455 } { 456 t.Run(name, func(t *testing.T) { 457 splits, err := SplitWebhookPost(tc.Post, maxPostSize) 458 if tc.Expected == nil { 459 require.NotNil(t, err) 460 } else { 461 require.Nil(t, err) 462 } 463 assert.Equal(t, len(tc.Expected), len(splits)) 464 for i, split := range splits { 465 if i < len(tc.Expected) { 466 assert.Equal(t, tc.Expected[i].Message, split.Message) 467 assert.Equal(t, tc.Expected[i].Props["attachments"], split.Props["attachments"]) 468 } 469 } 470 }) 471 } 472 }