github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/notify/notify_test.go (about) 1 package notify 2 3 import ( 4 "testing" 5 "time" 6 7 "github.com/evergreen-ci/evergreen" 8 "github.com/evergreen-ci/evergreen/db" 9 "github.com/evergreen-ci/evergreen/model" 10 "github.com/evergreen-ci/evergreen/model/build" 11 "github.com/evergreen-ci/evergreen/model/task" 12 "github.com/evergreen-ci/evergreen/model/version" 13 "github.com/evergreen-ci/evergreen/testutil" 14 "github.com/mongodb/grip" 15 . "github.com/smartystreets/goconvey/convey" 16 ) 17 18 var ( 19 buildId = "build" 20 taskId = "task" 21 projectId = "project" 22 buildVariant = "buildVariant" 23 displayName = "displayName" 24 emailSubjects = make([]string, 0) 25 emailBodies = make([]string, 0) 26 27 buildFailureNotificationKey = NotificationKey{ 28 Project: projectId, 29 NotificationName: buildFailureKey, 30 NotificationType: buildType, 31 NotificationRequester: evergreen.RepotrackerVersionRequester, 32 } 33 buildSucceessNotificationKey = NotificationKey{ 34 Project: projectId, 35 NotificationName: buildSuccessKey, 36 NotificationType: buildType, 37 NotificationRequester: evergreen.RepotrackerVersionRequester, 38 } 39 buildCompletionNotificationKey = NotificationKey{ 40 Project: projectId, 41 NotificationName: buildCompletionKey, 42 NotificationType: buildType, 43 NotificationRequester: evergreen.RepotrackerVersionRequester, 44 } 45 buildSuccessToFailureNotificationKey = NotificationKey{ 46 Project: projectId, 47 NotificationName: buildSuccessToFailureKey, 48 NotificationType: buildType, 49 NotificationRequester: evergreen.RepotrackerVersionRequester, 50 } 51 taskFailureNotificationKey = NotificationKey{ 52 Project: projectId, 53 NotificationName: taskFailureKey, 54 NotificationType: taskType, 55 NotificationRequester: evergreen.RepotrackerVersionRequester, 56 } 57 taskSucceessNotificationKey = NotificationKey{ 58 Project: projectId, 59 NotificationName: taskSuccessKey, 60 NotificationType: taskType, 61 NotificationRequester: evergreen.RepotrackerVersionRequester, 62 } 63 taskCompletionNotificationKey = NotificationKey{ 64 Project: projectId, 65 NotificationName: taskCompletionKey, 66 NotificationType: taskType, 67 NotificationRequester: evergreen.RepotrackerVersionRequester, 68 } 69 taskSuccessToFailureNotificationKey = NotificationKey{ 70 Project: projectId, 71 NotificationName: taskSuccessToFailureKey, 72 NotificationType: taskType, 73 NotificationRequester: evergreen.RepotrackerVersionRequester, 74 } 75 ) 76 77 var TestConfig = testutil.TestConfig() 78 79 func TestNotify(t *testing.T) { 80 grip.CatchError(grip.SetSender(testutil.SetupTestSender(TestConfig.Notify.LogFile))) 81 82 db.SetGlobalSessionProvider(db.SessionFactoryFromConfig(TestConfig)) 83 emailSubjects = make([]string, 0) 84 emailBodies = make([]string, 0) 85 86 Convey("When running notification handlers", t, func() { 87 88 ae, err := createEnvironment(TestConfig, map[string]interface{}{}) 89 So(err, ShouldBeNil) 90 91 Convey("Build-specific handlers should return the correct emails", func() { 92 cleanupdb() 93 timeNow := time.Now() 94 // insert the test documents 95 insertBuildDocs(timeNow) 96 version := &version.Version{Id: "version"} 97 So(version.Insert(), ShouldBeNil) 98 Convey("BuildFailureHandler should return 1 email per failed build", func() { 99 handler := BuildFailureHandler{} 100 emails, err := handler.GetNotifications(ae, &buildFailureNotificationKey) 101 So(err, ShouldBeNil) 102 // check that we only returned 2 failed notifications 103 So(len(emails), ShouldEqual, 2) 104 So(emails[0].GetSubject(), ShouldEqual, 105 "[MCI-FAILURE ] Build #build1 failed on displayName") 106 So(emails[1].GetSubject(), ShouldEqual, 107 "[MCI-FAILURE ] Build #build9 failed on displayName") 108 }) 109 110 Convey("BuildSuccessHandler should return 1 email per successful build", func() { 111 handler := BuildSuccessHandler{} 112 emails, err := handler.GetNotifications(ae, &buildSucceessNotificationKey) 113 So(err, ShouldBeNil) 114 // check that we only returned 2 success notifications 115 So(len(emails), ShouldEqual, 2) 116 So(emails[0].GetSubject(), ShouldEqual, 117 "[MCI-SUCCESS ] Build #build3 succeeded on displayName") 118 So(emails[1].GetSubject(), ShouldEqual, 119 "[MCI-SUCCESS ] Build #build8 succeeded on displayName") 120 }) 121 122 Convey("BuildCompletionHandler should return 1 email per completed build", func() { 123 handler := BuildCompletionHandler{} 124 emails, err := handler.GetNotifications(ae, &buildCompletionNotificationKey) 125 So(err, ShouldBeNil) 126 // check that we only returned 6 completed notifications 127 So(len(emails), ShouldEqual, 4) 128 So(emails[0].GetSubject(), ShouldEqual, 129 "[MCI-COMPLETION ] Build #build1 completed on displayName") 130 So(emails[1].GetSubject(), ShouldEqual, 131 "[MCI-COMPLETION ] Build #build3 completed on displayName") 132 So(emails[2].GetSubject(), ShouldEqual, 133 "[MCI-COMPLETION ] Build #build8 completed on displayName") 134 So(emails[3].GetSubject(), ShouldEqual, 135 "[MCI-COMPLETION ] Build #build9 completed on displayName") 136 }) 137 138 Convey("BuildSuccessToFailureHandler should return 1 email per "+ 139 "build success to failure transition", func() { 140 handler := BuildSuccessToFailureHandler{} 141 emails, err := handler.GetNotifications(ae, &buildSuccessToFailureNotificationKey) 142 So(err, ShouldBeNil) 143 // check that we only returned 1 success_to_failure notifications 144 So(len(emails), ShouldEqual, 1) 145 So(emails[0].GetSubject(), ShouldEqual, 146 "[MCI-FAILURE ] Build #build9 transitioned to failure on displayName") 147 }) 148 }) 149 150 Convey("Task-specific handlers should return the correct emails", func() { 151 cleanupdb() 152 timeNow := time.Now() 153 // insert the test documents 154 insertTaskDocs(timeNow) 155 v := &version.Version{Id: "version"} 156 So(v.Insert(), ShouldBeNil) 157 158 Convey("TaskFailureHandler should return 1 email per task failure", func() { 159 handler := TaskFailureHandler{} 160 emails, err := handler.GetNotifications(ae, &taskFailureNotificationKey) 161 So(err, ShouldBeNil) 162 // check that we only returned 2 failed notifications 163 So(len(emails), ShouldEqual, 2) 164 So(emails[0].GetSubject(), ShouldEqual, 165 "[MCI-FAILURE ] possible MCI failure in displayName (failed on build1)") 166 So(emails[1].GetSubject(), ShouldEqual, 167 "[MCI-FAILURE ] possible MCI failure in displayName (failed on build1)") 168 }) 169 170 Convey("TaskSuccessHandler should return 1 email per task success", func() { 171 handler := TaskSuccessHandler{} 172 emails, err := handler.GetNotifications(ae, &taskSucceessNotificationKey) 173 So(err, ShouldBeNil) 174 // check that we only returned 2 success notifications 175 So(len(emails), ShouldEqual, 2) 176 So(emails[0].GetSubject(), ShouldEqual, 177 "[MCI-SUCCESS ] possible MCI failure in displayName (succeeded on build1)") 178 So(emails[1].GetSubject(), ShouldEqual, 179 "[MCI-SUCCESS ] possible MCI failure in displayName (succeeded on build1)") 180 }) 181 182 Convey("TaskCompletionHandler should return 1 email per completed task", func() { 183 handler := TaskCompletionHandler{} 184 emails, err := handler.GetNotifications(ae, &taskCompletionNotificationKey) 185 So(err, ShouldBeNil) 186 // check that we only returned 6 completion notifications 187 So(len(emails), ShouldEqual, 4) 188 So(emails[0].GetSubject(), ShouldEqual, 189 "[MCI-COMPLETION ] possible MCI failure in displayName (completed on build1)") 190 So(emails[1].GetSubject(), ShouldEqual, 191 "[MCI-COMPLETION ] possible MCI failure in displayName (completed on build1)") 192 So(emails[2].GetSubject(), ShouldEqual, 193 "[MCI-COMPLETION ] possible MCI failure in displayName (completed on build1)") 194 So(emails[3].GetSubject(), ShouldEqual, 195 "[MCI-COMPLETION ] possible MCI failure in displayName (completed on build1)") 196 }) 197 198 Convey("TaskSuccessToFailureHandler should return 1 email per "+ 199 "task success to failure transition", func() { 200 handler := TaskSuccessToFailureHandler{} 201 emails, err := handler.GetNotifications(ae, &taskSuccessToFailureNotificationKey) 202 So(err, ShouldBeNil) 203 // check that we only returned 1 success to failure notifications 204 So(len(emails), ShouldEqual, 1) 205 So(emails[0].GetSubject(), ShouldEqual, 206 "[MCI-FAILURE ] possible MCI failure in displayName (transitioned to "+ 207 "failure on build1)") 208 }) 209 }) 210 }) 211 212 Convey("When running notifications pipeline", t, func() { 213 cleanupdb() 214 timeNow := time.Now() 215 // insert the test documents 216 insertTaskDocs(timeNow) 217 v := &version.Version{Id: "version"} 218 So(v.Insert(), ShouldBeNil) 219 220 Convey("Should run the correct notification handlers for given "+ 221 "notification keys", func() { 222 notificationSettings := &MCINotification{} 223 notificationSettings.Notifications = []Notification{ 224 {"task_failure", "project", []string{"user@mongodb"}, []string{}}, 225 {"task_success_to_failure", "project", []string{"user@mongodb"}, []string{}}, 226 } 227 notificationSettings.Teams = []Team{ 228 { 229 "myteam", 230 "myteam@me.com", 231 []Subscription{{"task", []string{}, []string{"task_failure"}}}, 232 }, 233 } 234 notificationSettings.PatchNotifications = []Subscription{ 235 {"patch_project", []string{}, []string{}}, 236 } 237 238 notificationKeyFailure := NotificationKey{"project", "task_failure", "task", "gitter_request"} 239 notificationKeyToFailure := NotificationKey{"project", "task_success_to_failure", "task", 240 "gitter_request"} 241 242 ae, err := createEnvironment(TestConfig, map[string]interface{}{}) 243 So(err, ShouldBeNil) 244 245 emails, err := ProcessNotifications(ae, notificationSettings, false) 246 So(err, ShouldBeNil) 247 248 So(len(emails[notificationKeyFailure]), ShouldEqual, 2) 249 So(emails[notificationKeyFailure][0].GetSubject(), ShouldEqual, 250 "[MCI-FAILURE ] possible MCI failure in displayName (failed on build1)") 251 So(emails[notificationKeyFailure][1].GetSubject(), ShouldEqual, 252 "[MCI-FAILURE ] possible MCI failure in displayName (failed on build1)") 253 254 So(len(emails[notificationKeyToFailure]), ShouldEqual, 1) 255 So(emails[notificationKeyToFailure][0].GetSubject(), ShouldEqual, 256 "[MCI-FAILURE ] possible MCI failure in displayName (transitioned to "+ 257 "failure on build1)") 258 }) 259 260 Convey("SendNotifications should send emails correctly", func() { 261 notificationSettings := &MCINotification{} 262 notificationSettings.Notifications = []Notification{ 263 {"task_failure", "project", []string{"user@mongodb"}, []string{}}, 264 } 265 notificationSettings.Teams = []Team{ 266 { 267 "myteam", 268 "myteam@me.com", 269 []Subscription{{"task", []string{}, []string{"task_failure"}}}, 270 }, 271 } 272 notificationSettings.PatchNotifications = []Subscription{ 273 {"patch_project", []string{}, []string{}}, 274 } 275 276 fakeTask, err := task.FindOne(task.ById("task8")) 277 So(err, ShouldBeNil) 278 notificationKey := NotificationKey{"project", "task_failure", "task", "gitter_request"} 279 280 triggeredNotification := TriggeredTaskNotification{ 281 fakeTask, 282 nil, 283 []ChangeInfo{}, 284 notificationKey, 285 "[MCI-FAILURE]", 286 "failed", 287 } 288 289 email := TaskEmail{ 290 EmailBase{ 291 "This is the email body", 292 "This is the email subject", 293 triggeredNotification.Info, 294 }, 295 triggeredNotification, 296 } 297 298 m := make(map[NotificationKey][]Email) 299 m[notificationKey] = []Email{&email} 300 301 mailer := MockMailer{} 302 mockSettings := evergreen.Settings{Notify: evergreen.NotifyConfig{}} 303 err = SendNotifications(&mockSettings, notificationSettings, m, mailer) 304 So(err, ShouldBeNil) 305 306 So(len(emailSubjects), ShouldEqual, 1) 307 So(emailSubjects[0], ShouldEqual, 308 "This is the email subject") 309 So(emailBodies[0], ShouldEqual, 310 "This is the email body") 311 }) 312 }) 313 } 314 315 func insertBuildDocs(priorTime time.Time) { 316 // add test build docs to the build collection 317 318 // build 1 319 // build finished unsuccessfully (failed) 320 // should trigger the following handler(s): 321 // - buildFailureHandler 322 // - buildCompletionHandler 323 insertBuild(buildId+"1", projectId, displayName, buildVariant, 324 evergreen.BuildFailed, time.Now(), time.Now(), time.Duration(10), true, 325 evergreen.RepotrackerVersionRequester, 1) 326 327 // build 2 328 // build not finished 329 insertBuild(buildId+"2", projectId, displayName, buildVariant, 330 evergreen.BuildStarted, time.Now(), time.Now(), time.Duration(0), true, 331 evergreen.RepotrackerVersionRequester, 2) 332 333 // build 3 334 // build finished successfully (success) 335 // should trigger the following handler(s): 336 // - buildSuccessHandler 337 // - buildCompletionHandler 338 insertBuild(buildId+"3", projectId, displayName, buildVariant, 339 evergreen.BuildSucceeded, time.Now(), time.Now(), time.Duration(50), true, 340 evergreen.RepotrackerVersionRequester, 3) 341 342 // build 5 343 // build not finished 344 insertBuild(buildId+"5", projectId, displayName, buildVariant, 345 evergreen.BuildStarted, time.Now(), time.Now(), time.Duration(0), true, 346 evergreen.RepotrackerVersionRequester, 5) 347 348 // build 6 349 // build finished (failed) from different project 350 // should trigger the following handler(s): 351 // - buildFailureHandler 352 // - buildCompletionHandler 353 insertBuild(buildId+"6", projectId+"_", displayName, buildVariant, 354 evergreen.BuildFailed, time.Now(), time.Now(), time.Duration(10), true, 355 evergreen.RepotrackerVersionRequester, 6) 356 357 // build 7 358 // build finished (succeeded) from different project 359 // should trigger the following handler(s): 360 // - buildSuccessHandler 361 insertBuild(buildId+"7", projectId+"_", displayName, buildVariant, 362 evergreen.BuildSucceeded, time.Now(), time.Now(), time.Duration(10), true, 363 evergreen.RepotrackerVersionRequester, 7) 364 365 // build 8 366 // build finished (succeeded) from different build variant 367 // should trigger the following handler(s): 368 // - buildSuccessToFailureHandler (in conjunction with 9) 369 // - buildCompletionHandler 370 insertBuild(buildId+"8", projectId, displayName, buildVariant+"_", 371 evergreen.BuildSucceeded, time.Now(), time.Now(), time.Duration(10), true, 372 evergreen.RepotrackerVersionRequester, 8) 373 374 // build 9 375 // build finished (failed) from different build variant 376 // should trigger the following handler(s): 377 // - buildSuccessToFailureHandler (in conjunction with 8) 378 // - buildCompletionHandler 379 insertBuild(buildId+"9", projectId, displayName, buildVariant+"_", 380 evergreen.BuildFailed, time.Now(), time.Now(), time.Duration(10), true, 381 evergreen.RepotrackerVersionRequester, 9) 382 383 insertVersions() 384 } 385 386 func insertTaskDocs(priorTime time.Time) { 387 // add test task docs to the task collection 388 389 // task 1 390 // task finished unsuccessfully (failed) 391 // should trigger the following handler(s): 392 // - taskFailureHandler 393 // - taskCompletionHandler 394 insertTask(taskId+"1", projectId, displayName, buildVariant, evergreen.TaskFailed, 395 time.Now(), time.Now(), time.Now(), time.Duration(10), true, 396 evergreen.RepotrackerVersionRequester, 1) 397 398 // task 2 399 // task not finished 400 insertTask(taskId+"2", projectId, displayName, buildVariant, evergreen.TaskStarted, 401 time.Now(), time.Now(), time.Now(), time.Duration(0), true, 402 evergreen.RepotrackerVersionRequester, 2) 403 404 // task 3 405 // task finished successfully (success) 406 // should trigger the following handler(s): 407 // - taskSuccessHandler 408 // - taskCompletionHandler 409 insertTask(taskId+"3", projectId, displayName, buildVariant, 410 evergreen.TaskSucceeded, time.Now(), time.Now(), time.Now(), time.Duration(50), 411 true, evergreen.RepotrackerVersionRequester, 3) 412 413 // task 5 414 // task not finished 415 insertTask(taskId+"5", projectId, displayName, buildVariant, evergreen.TaskStarted, 416 time.Now(), time.Now(), time.Now(), time.Duration(0), true, 417 evergreen.RepotrackerVersionRequester, 5) 418 419 // task 6 420 // task finished (failed) from different project 421 insertTask(taskId+"6", projectId+"_", displayName, buildVariant, 422 evergreen.TaskFailed, time.Now(), time.Now(), time.Now(), time.Duration(10), 423 true, evergreen.RepotrackerVersionRequester, 6) 424 425 // task 7 426 // task finished (succeeded) from different project 427 insertTask(taskId+"7", projectId+"_", displayName, buildVariant, 428 evergreen.TaskSucceeded, time.Now(), time.Now(), time.Now(), time.Duration(10), 429 true, evergreen.RepotrackerVersionRequester, 7) 430 431 // task 8 432 // task finished (succeeded) from different build variant 433 // should trigger the following handler(s): 434 // - taskSuccessHandler 435 // - taskCompletionHandler 436 // - taskSuccessToFailureHandler (in conjunction with 9) 437 insertTask(taskId+"8", projectId, displayName, buildVariant+"_", 438 evergreen.TaskSucceeded, time.Now(), time.Now(), time.Now(), time.Duration(10), 439 true, evergreen.RepotrackerVersionRequester, 8) 440 441 // task 9 442 // task finished (failed) from different build variant 443 // should trigger the following handler(s): 444 // - taskFailedHandler 445 // - taskCompletionHandler 446 // - taskSuccessToFailureHandler (in conjunction with 8) 447 insertTask(taskId+"9", projectId, displayName, buildVariant+"_", 448 evergreen.TaskFailed, time.Now(), time.Now(), 449 time.Now().Add(time.Duration(3*time.Minute)), time.Duration(10), true, 450 evergreen.RepotrackerVersionRequester, 9) 451 452 insertVersions() 453 } 454 455 func insertBuild(id, project, display_name, buildVariant, status string, createTime, 456 finishTime time.Time, timeTaken time.Duration, activated bool, requester string, 457 order int) { 458 build := &build.Build{ 459 Id: id, 460 BuildNumber: id, 461 Project: project, 462 BuildVariant: buildVariant, 463 TimeTaken: timeTaken, 464 Status: status, 465 CreateTime: createTime, 466 DisplayName: display_name, 467 FinishTime: finishTime, 468 Activated: activated, 469 Requester: requester, 470 RevisionOrderNumber: order, 471 Version: "version", 472 } 473 So(build.Insert(), ShouldBeNil) 474 } 475 476 func insertTask(id, project, display_name, buildVariant, status string, createTime, 477 finishTime, pushTime time.Time, timeTaken time.Duration, activated bool, 478 requester string, order int) { 479 newTask := &task.Task{ 480 Id: id, 481 Project: project, 482 DisplayName: display_name, 483 Status: status, 484 BuildVariant: buildVariant, 485 CreateTime: createTime, 486 PushTime: pushTime, 487 FinishTime: finishTime, 488 TimeTaken: timeTaken, 489 Activated: activated, 490 Requester: requester, 491 RevisionOrderNumber: order, 492 BuildId: "build1", 493 Version: "version", 494 } 495 So(newTask.Insert(), ShouldBeNil) 496 } 497 498 func insertVersions() { 499 v := &version.Version{ 500 Id: "version1", 501 Identifier: "", 502 BuildIds: []string{"build1"}, 503 Author: "user@mci", 504 Message: "Fixed all the bugs", 505 } 506 507 So(v.Insert(), ShouldBeNil) 508 509 version2 := &version.Version{ 510 Id: "version2", 511 Identifier: "", 512 BuildIds: []string{"build2", "build3", "build5", "build6", 513 "build7", "build8", "build9"}, 514 Author: "user@mci", 515 Message: "Fixed all the other bugs", 516 } 517 518 So(version2.Insert(), ShouldBeNil) 519 } 520 521 func cleanupdb() { 522 err := db.ClearCollections( 523 task.Collection, 524 model.NotifyTimesCollection, 525 model.NotifyHistoryCollection, 526 build.Collection, 527 version.Collection) 528 So(err, ShouldBeNil) 529 } 530 531 type MockMailer struct{} 532 533 func (self MockMailer) SendMail(recipients []string, subject, body string) error { 534 emailSubjects = append(emailSubjects, subject) 535 emailBodies = append(emailBodies, body) 536 return nil 537 }