github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/alerts/jira_test.go (about) 1 package alerts 2 3 import ( 4 "testing" 5 6 "github.com/evergreen-ci/evergreen" 7 "github.com/evergreen-ci/evergreen/apimodels" 8 "github.com/evergreen-ci/evergreen/model" 9 "github.com/evergreen-ci/evergreen/model/alert" 10 "github.com/evergreen-ci/evergreen/model/alertrecord" 11 "github.com/evergreen-ci/evergreen/model/build" 12 "github.com/evergreen-ci/evergreen/model/host" 13 "github.com/evergreen-ci/evergreen/model/task" 14 "github.com/evergreen-ci/evergreen/model/version" 15 "github.com/evergreen-ci/evergreen/thirdparty" 16 "github.com/pkg/errors" 17 . "github.com/smartystreets/goconvey/convey" 18 ) 19 20 func TestJIRASummary(t *testing.T) { 21 Convey("With failed task alert types:", t, func() { 22 ctx := AlertContext{ 23 AlertRequest: &alert.AlertRequest{ 24 Trigger: alertrecord.TaskFailedId, 25 }, 26 ProjectRef: &model.ProjectRef{ 27 DisplayName: ProjectName, 28 Owner: ProjectOwner, 29 }, 30 Task: &task.Task{ 31 DisplayName: TaskName, 32 Details: apimodels.TaskEndDetail{}, 33 }, 34 Build: &build.Build{DisplayName: BuildName}, 35 Version: &version.Version{Revision: VersionRevision}, 36 } 37 38 Convey("a task that timed out should return a subject", func() { 39 ctx.Task.Details.TimedOut = true 40 subj := getSummary(ctx) 41 So(subj, ShouldNotEqual, "") 42 Convey("denoting the time out and showing the task name", func() { 43 So(subj, ShouldContainSubstring, "Timed Out") 44 So(subj, ShouldContainSubstring, TaskName) 45 So(subj, ShouldContainSubstring, BuildName) 46 So(subj, ShouldContainSubstring, VersionRevision[0:8]) 47 So(subj, ShouldNotContainSubstring, VersionRevision[0:9]) 48 So(subj, ShouldContainSubstring, ProjectName) 49 }) 50 }) 51 Convey("a task that failed on a system command should return a subject", func() { 52 ctx.Task.Details.Type = model.SystemCommandType 53 subj := getSummary(ctx) 54 So(subj, ShouldNotEqual, "") 55 Convey("denoting the system failure and showing the task name", func() { 56 So(subj, ShouldContainSubstring, "System") 57 So(subj, ShouldContainSubstring, TaskName) 58 So(subj, ShouldContainSubstring, BuildName) 59 So(subj, ShouldContainSubstring, VersionRevision[0:8]) 60 So(subj, ShouldContainSubstring, ProjectName) 61 }) 62 }) 63 Convey("a task that failed on a normal command with no tests should return a subject", func() { 64 subj := getSummary(ctx) 65 So(subj, ShouldNotEqual, "") 66 Convey("denoting the failure and showing the task name", func() { 67 So(subj, ShouldContainSubstring, "Failed") 68 So(subj, ShouldContainSubstring, TaskName) 69 So(subj, ShouldContainSubstring, BuildName) 70 So(subj, ShouldContainSubstring, VersionRevision[0:8]) 71 So(subj, ShouldContainSubstring, ProjectName) 72 }) 73 }) 74 Convey("a task with two failed tests should return a subject", func() { 75 ctx.Task.TestResults = []task.TestResult{ 76 {TestFile: TestName1, Status: evergreen.TestFailedStatus}, 77 {TestFile: TestName2, Status: evergreen.TestFailedStatus}, 78 {TestFile: TestName3, Status: evergreen.TestSucceededStatus}, 79 {TestFile: TestName3, Status: evergreen.TestSucceededStatus}, 80 {TestFile: TestName3, Status: evergreen.TestSucceededStatus}, 81 {TestFile: TestName3, Status: evergreen.TestSucceededStatus}, 82 } 83 subj := getSummary(ctx) 84 So(subj, ShouldNotEqual, "") 85 Convey("denoting the failure and showing the task name and failed tests", func() { 86 So(subj, ShouldContainSubstring, "Failures") 87 So(subj, ShouldContainSubstring, TaskName) 88 So(subj, ShouldContainSubstring, BuildName) 89 So(subj, ShouldContainSubstring, VersionRevision[0:8]) 90 So(subj, ShouldContainSubstring, ProjectName) 91 So(subj, ShouldContainSubstring, "big_test.js") 92 So(subj, ShouldContainSubstring, "FunUnitTest") 93 So(subj, ShouldNotContainSubstring, "cool.exe") 94 Convey("with test names properly truncated", func() { 95 So(subj, ShouldNotContainSubstring, "local") 96 So(subj, ShouldNotContainSubstring, "jstest") 97 }) 98 }) 99 }) 100 Convey("a task with failing tests should return a subject omitting any silently failing tests", func() { 101 ctx.Task.TestResults = []task.TestResult{ 102 {TestFile: TestName1, Status: evergreen.TestFailedStatus}, 103 {TestFile: TestName2, Status: evergreen.TestFailedStatus}, 104 {TestFile: TestName3, Status: evergreen.TestSilentlyFailedStatus}, 105 } 106 subj := getSummary(ctx) 107 So(subj, ShouldNotEqual, "") 108 Convey("denoting the failure and showing the task name and failed tests", func() { 109 So(subj, ShouldContainSubstring, "Failures") 110 So(subj, ShouldContainSubstring, TaskName) 111 So(subj, ShouldContainSubstring, BuildName) 112 So(subj, ShouldContainSubstring, VersionRevision[0:8]) 113 So(subj, ShouldContainSubstring, ProjectName) 114 So(subj, ShouldContainSubstring, "big_test.js") 115 So(subj, ShouldContainSubstring, "FunUnitTest") 116 So(subj, ShouldNotContainSubstring, "cool.exe") 117 Convey("with test names properly truncated", func() { 118 So(subj, ShouldNotContainSubstring, "local") 119 So(subj, ShouldNotContainSubstring, "jstest") 120 }) 121 }) 122 }) 123 Convey("a task with five failed tests should return a subject", func() { 124 ctx.Task.TestResults = []task.TestResult{ 125 {TestFile: TestName1, Status: evergreen.TestFailedStatus}, 126 {TestFile: TestName2, Status: evergreen.TestFailedStatus}, 127 {TestFile: TestName3, Status: evergreen.TestFailedStatus}, 128 {TestFile: TestName3, Status: evergreen.TestFailedStatus}, 129 {TestFile: TestName3, Status: evergreen.TestFailedStatus}, 130 } 131 subj := getSummary(ctx) 132 So(subj, ShouldNotEqual, "") 133 Convey("denoting two test failures but hiding the rest", func() { 134 So(subj, ShouldContainSubstring, "Failures") 135 So(subj, ShouldContainSubstring, TaskName) 136 So(subj, ShouldContainSubstring, BuildName) 137 So(subj, ShouldContainSubstring, VersionRevision[0:8]) 138 So(subj, ShouldContainSubstring, ProjectName) 139 So(subj, ShouldContainSubstring, "big_test.js") 140 So(subj, ShouldContainSubstring, "FunUnitTest") 141 So(subj, ShouldNotContainSubstring, "cool.exe") 142 So(subj, ShouldContainSubstring, "+3 more") 143 }) 144 }) 145 Convey("a failed task with passing tests should return a subject", func() { 146 ctx.Task.TestResults = []task.TestResult{ 147 {TestFile: TestName1, Status: evergreen.TestSucceededStatus}, 148 {TestFile: TestName2, Status: evergreen.TestSucceededStatus}, 149 {TestFile: TestName3, Status: evergreen.TestSucceededStatus}, 150 } 151 subj := getSummary(ctx) 152 So(subj, ShouldNotEqual, "") 153 Convey("denoting a task failure without a parenthetical", func() { 154 So(subj, ShouldContainSubstring, "Failed") 155 So(subj, ShouldContainSubstring, TaskName) 156 So(subj, ShouldContainSubstring, BuildName) 157 So(subj, ShouldContainSubstring, VersionRevision[0:8]) 158 So(subj, ShouldContainSubstring, ProjectName) 159 So(subj, ShouldNotContainSubstring, "big_test.js") 160 So(subj, ShouldNotContainSubstring, "FunUnitTest") 161 So(subj, ShouldNotContainSubstring, "cool.exe") 162 So(subj, ShouldNotContainSubstring, "(") 163 So(subj, ShouldNotContainSubstring, ")") 164 }) 165 }) 166 Convey("a failed task with only passing or silently failing tests should return a subject", func() { 167 ctx.Task.TestResults = []task.TestResult{ 168 {TestFile: TestName1, Status: evergreen.TestSilentlyFailedStatus}, 169 {TestFile: TestName2, Status: evergreen.TestSucceededStatus}, 170 {TestFile: TestName3, Status: evergreen.TestSilentlyFailedStatus}, 171 } 172 subj := getSummary(ctx) 173 So(subj, ShouldNotEqual, "") 174 Convey("denoting a task failure without a parenthetical", func() { 175 So(subj, ShouldContainSubstring, "Failed") 176 So(subj, ShouldContainSubstring, TaskName) 177 So(subj, ShouldContainSubstring, BuildName) 178 So(subj, ShouldContainSubstring, VersionRevision[0:8]) 179 So(subj, ShouldContainSubstring, ProjectName) 180 So(subj, ShouldNotContainSubstring, "big_test.js") 181 So(subj, ShouldNotContainSubstring, "FunUnitTest") 182 So(subj, ShouldNotContainSubstring, "cool.exe") 183 So(subj, ShouldNotContainSubstring, "(") 184 So(subj, ShouldNotContainSubstring, ")") 185 }) 186 }) 187 }) 188 } 189 190 const ( 191 HostId = "h1" 192 HostDNS = "h1.net" 193 ) 194 195 func TestJIRADescription(t *testing.T) { 196 Convey("With a failed task context", t, func() { 197 ui := "http://evergreen.ui" 198 ctx := AlertContext{ 199 AlertRequest: &alert.AlertRequest{ 200 Trigger: alertrecord.TaskFailedId, 201 }, 202 ProjectRef: &model.ProjectRef{ 203 DisplayName: ProjectName, 204 Identifier: ProjectId, 205 Owner: ProjectOwner, 206 }, 207 Task: &task.Task{ 208 Id: TaskId, 209 DisplayName: TaskName, 210 Details: apimodels.TaskEndDetail{}, 211 Project: ProjectId, 212 TestResults: []task.TestResult{ 213 {TestFile: TestName1, Status: evergreen.TestFailedStatus, URL: "direct_link"}, 214 {TestFile: TestName2, Status: evergreen.TestFailedStatus, LogId: "123"}, 215 {TestFile: TestName3, Status: evergreen.TestSucceededStatus}, 216 }, 217 }, 218 Host: &host.Host{Id: HostId, Host: HostDNS}, 219 Build: &build.Build{DisplayName: BuildName, Id: BuildId}, 220 Version: &version.Version{ 221 Revision: VersionRevision, 222 Message: VersionMessage, 223 }, 224 } 225 Convey("the description should be successfully generated", func() { 226 d, err := getDescription(ctx, ui) 227 So(err, ShouldBeNil) 228 So(d, ShouldNotEqual, "") 229 230 Convey("the task, host, project, and build names should be present", func() { 231 So(d, ShouldContainSubstring, TaskName) 232 So(d, ShouldContainSubstring, HostDNS) 233 So(d, ShouldContainSubstring, ProjectName) 234 So(d, ShouldContainSubstring, BuildName) 235 So(d, ShouldContainSubstring, ProjectOwner) 236 So(d, ShouldContainSubstring, VersionRevision) 237 So(d, ShouldContainSubstring, VersionMessage) 238 So(d, ShouldContainSubstring, "diff|https://github.com/") 239 }) 240 Convey("with links to the task, host, project", func() { 241 So(d, ShouldContainSubstring, TaskId) 242 So(d, ShouldContainSubstring, HostId) 243 So(d, ShouldContainSubstring, ProjectId) 244 }) 245 Convey("and the failed tasks should be listed with links", func() { 246 So(d, ShouldContainSubstring, cleanTestName(TestName1)) 247 So(d, ShouldContainSubstring, "direct_link") 248 So(d, ShouldContainSubstring, cleanTestName(TestName2)) 249 So(d, ShouldContainSubstring, "test_log/123") 250 Convey("but passing tasks should not be present", func() { 251 So(d, ShouldNotContainSubstring, cleanTestName(TestName3)) 252 }) 253 }) 254 }) 255 }) 256 } 257 258 type mockJIRAHandler struct { 259 err error 260 proj string 261 desc string 262 summary string 263 } 264 265 func (mj *mockJIRAHandler) CreateTicket(fields map[string]interface{}) ( 266 *thirdparty.JiraCreateTicketResponse, error) { 267 if mj.err != nil { 268 return nil, mj.err 269 } 270 // if these lookups panic then that's fine -- the test will fail 271 mj.proj = fields["project"].(map[string]string)["key"] 272 mj.summary = fields["summary"].(string) 273 mj.desc = fields["description"].(string) 274 return &thirdparty.JiraCreateTicketResponse{Key: mj.proj + "-1"}, nil 275 } 276 277 func (mj *mockJIRAHandler) JiraHost() string { return "mock" } 278 279 func TestJIRADelivery(t *testing.T) { 280 Convey("With a failed task context and alertConf", t, func() { 281 ui := "http://evergreen.ui" 282 ctx := AlertContext{ 283 AlertRequest: &alert.AlertRequest{ 284 Trigger: alertrecord.TaskFailedId, 285 }, 286 ProjectRef: &model.ProjectRef{DisplayName: ProjectName, Identifier: ProjectId}, 287 Task: &task.Task{ 288 Id: TaskId, 289 DisplayName: TaskName, 290 Details: apimodels.TaskEndDetail{}, 291 Project: ProjectId, 292 }, 293 Host: &host.Host{Id: HostId, Host: HostDNS}, 294 Build: &build.Build{DisplayName: BuildName, Id: BuildId}, 295 Version: &version.Version{Revision: VersionRevision}, 296 } 297 alertConf := model.AlertConfig{Provider: JiraProvider} 298 Convey("and a JIRA Deliverer with a valid mock jiraCreator", func() { 299 mjc := &mockJIRAHandler{} 300 jd := &jiraDeliverer{ 301 project: "COOL", 302 handler: mjc, 303 uiRoot: ui, 304 } 305 Convey("a populated ticket should be created successfully", func() { 306 So(jd.Deliver(ctx, alertConf), ShouldBeNil) 307 So(mjc.desc, ShouldNotEqual, "") 308 So(mjc.summary, ShouldNotEqual, "") 309 So(mjc.proj, ShouldEqual, "COOL") 310 }) 311 }) 312 Convey("and a JIRA Deliverer with an erroring jiraCreator", func() { 313 mjc := &mockJIRAHandler{err: errors.New("bad internet uh-oh")} 314 jd := &jiraDeliverer{ 315 project: "COOL", 316 handler: mjc, 317 uiRoot: ui, 318 } 319 Convey("should return an error", func() { 320 So(jd.Deliver(ctx, alertConf), ShouldNotBeNil) 321 }) 322 }) 323 }) 324 } 325 326 func TestGetJIRADeliverer(t *testing.T) { 327 Convey("With a JIRA alertConf and QueueProcessor", t, func() { 328 alertConf := model.AlertConfig{ 329 Provider: JiraProvider, 330 Settings: map[string]interface{}{"project": "TEST", "issue": "Bug"}, 331 } 332 qp := &QueueProcessor{} 333 Convey("a QueueProcessor with full settings should return the JIRA deliverer", func() { 334 qp.config = &evergreen.Settings{ 335 Jira: evergreen.JiraConfig{ 336 Username: "u1", 337 Password: "pw", 338 Host: "www.example.com", 339 }, 340 Ui: evergreen.UIConfig{ 341 Url: "root", 342 }, 343 } 344 d, err := qp.getDeliverer(alertConf) 345 So(err, ShouldBeNil) 346 So(d, ShouldNotBeNil) 347 jd, ok := d.(*jiraDeliverer) 348 So(ok, ShouldBeTrue) 349 So(jd.handler, ShouldNotBeNil) 350 So(jd.project, ShouldEqual, "TEST") 351 So(jd.uiRoot, ShouldEqual, "root") 352 }) 353 354 Convey("a QueueProcessor with a malformed alertConf should error", func() { 355 alertConf.Settings = map[string]interface{}{"NOTproject": "TEST"} 356 d, err := qp.getDeliverer(alertConf) 357 So(err, ShouldNotBeNil) 358 So(d, ShouldBeNil) 359 alertConf.Settings = map[string]interface{}{"project": 1000} 360 d, err = qp.getDeliverer(alertConf) 361 So(err, ShouldNotBeNil) 362 So(d, ShouldBeNil) 363 }) 364 365 Convey("a QueueProcessor with missing JIRA settings should error", func() { 366 qp.config = &evergreen.Settings{ 367 Ui: evergreen.UIConfig{ 368 Url: "root", 369 }, 370 } 371 d, err := qp.getDeliverer(alertConf) 372 So(err, ShouldNotBeNil) 373 So(d, ShouldBeNil) 374 }) 375 Convey("a QueueProcessor with missing UI settings should error", func() { 376 qp.config = &evergreen.Settings{ 377 Jira: evergreen.JiraConfig{ 378 Username: "u1", 379 Password: "pw", 380 Host: "www.example.com", 381 }, 382 } 383 d, err := qp.getDeliverer(alertConf) 384 So(err, ShouldNotBeNil) 385 So(d, ShouldBeNil) 386 }) 387 }) 388 }