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  }