github.com/web-platform-tests/wpt.fyi@v0.0.0-20240530210107-70cf978996f1/api/taskcluster/webhook_test.go (about) 1 //go:build small 2 // +build small 3 4 // Copyright 2018 The WPT Dashboard Project. All rights reserved. 5 // Use of this source code is governed by a BSD-style license that can be 6 // found in the LICENSE file. 7 8 package taskcluster_test 9 10 import ( 11 "errors" 12 "fmt" 13 "io" 14 "net/http" 15 "net/http/httptest" 16 "net/url" 17 "strings" 18 "sync/atomic" 19 "testing" 20 "time" 21 22 "github.com/golang/mock/gomock" 23 "github.com/google/go-github/v47/github" 24 "github.com/stretchr/testify/assert" 25 uc "github.com/web-platform-tests/wpt.fyi/api/receiver/client" 26 tc "github.com/web-platform-tests/wpt.fyi/api/taskcluster" 27 mock_tc "github.com/web-platform-tests/wpt.fyi/api/taskcluster/mock_taskcluster" 28 "github.com/web-platform-tests/wpt.fyi/shared" 29 "github.com/web-platform-tests/wpt.fyi/shared/sharedtest" 30 ) 31 32 type branchInfos []*github.Branch 33 34 func strPtr(s string) *string { 35 return &s 36 } 37 38 func TestShouldProcessStatus_states(t *testing.T) { 39 status := tc.StatusEventPayload{} 40 status.State = strPtr("success") 41 status.Context = strPtr("Taskcluster") 42 status.Branches = branchInfos{&github.Branch{Name: strPtr(shared.MasterLabel)}} 43 assert.True(t, tc.ShouldProcessStatus(shared.NewNilLogger(), &status)) 44 45 status.Context = strPtr("Community-TC") 46 assert.True(t, tc.ShouldProcessStatus(shared.NewNilLogger(), &status)) 47 48 status.State = strPtr("failure") 49 assert.True(t, tc.ShouldProcessStatus(shared.NewNilLogger(), &status)) 50 51 status.State = strPtr("error") 52 assert.False(t, tc.ShouldProcessStatus(shared.NewNilLogger(), &status)) 53 54 status.State = strPtr("pending") 55 assert.False(t, tc.ShouldProcessStatus(shared.NewNilLogger(), &status)) 56 } 57 58 func TestShouldProcessStatus_notTaskcluster(t *testing.T) { 59 status := tc.StatusEventPayload{} 60 status.State = strPtr("success") 61 status.Context = strPtr("Travis") 62 status.Branches = branchInfos{&github.Branch{Name: strPtr(shared.MasterLabel)}} 63 assert.False(t, tc.ShouldProcessStatus(shared.NewNilLogger(), &status)) 64 } 65 66 func TestShouldProcessStatus_notOnMaster(t *testing.T) { 67 status := tc.StatusEventPayload{} 68 status.State = strPtr("success") 69 status.Context = strPtr("Taskcluster") 70 status.Branches = branchInfos{&github.Branch{Name: strPtr("gh-pages")}} 71 assert.True(t, tc.ShouldProcessStatus(shared.NewNilLogger(), &status)) 72 } 73 74 func TestIsOnMaster(t *testing.T) { 75 status := tc.StatusEventPayload{} 76 status.SHA = strPtr("a10867b14bb761a232cd80139fbd4c0d33264240") 77 status.State = strPtr("success") 78 status.Context = strPtr("Taskcluster") 79 status.Branches = branchInfos{ 80 &github.Branch{ 81 Name: strPtr(shared.MasterLabel), 82 Commit: &github.RepositoryCommit{SHA: strPtr("a10867b14bb761a232cd80139fbd4c0d33264240")}, 83 }, 84 &github.Branch{ 85 Name: strPtr("changes"), 86 Commit: &github.RepositoryCommit{SHA: strPtr("34c5c7793cb3b279e22454cb6750c80560547b3a")}, 87 }, 88 &github.Branch{ 89 Name: strPtr("gh-pages"), 90 Commit: &github.RepositoryCommit{SHA: strPtr("fd353d4ae7c19d2268397459524f849c129944a7")}, 91 }, 92 } 93 assert.True(t, status.IsOnMaster()) 94 95 status.Branches = status.Branches[1:] 96 assert.False(t, status.IsOnMaster()) 97 } 98 99 func TestParseTaskclusterURL(t *testing.T) { 100 t.Run("Status", func(t *testing.T) { 101 root, group, task := tc.ParseTaskclusterURL("https://tools.taskcluster.net/task-group-inspector/#/Y4rnZeqDRXGiRNiqxT5Qeg") 102 assert.Equal(t, "https://taskcluster.net", root) 103 assert.Equal(t, "Y4rnZeqDRXGiRNiqxT5Qeg", group) 104 assert.Equal(t, "", task) 105 }) 106 t.Run("CheckRun with task", func(t *testing.T) { 107 root, group, task := tc.ParseTaskclusterURL("https://tc.example.com/groups/IWlO7NuxRnO0_8PKMuHFkw/tasks/NOToWHr0T-u62B9yGQnD5w/details") 108 assert.Equal(t, "https://tc.example.com", root) 109 assert.Equal(t, "IWlO7NuxRnO0_8PKMuHFkw", group) 110 assert.Equal(t, "NOToWHr0T-u62B9yGQnD5w", task) 111 }) 112 t.Run("CheckRun without task", func(t *testing.T) { 113 root, group, task := tc.ParseTaskclusterURL("https://tc.other-example.com/groups/IWlO7NuxRnO0_8PKMuHFkw") 114 assert.Equal(t, "https://tc.other-example.com", root) 115 assert.Equal(t, "IWlO7NuxRnO0_8PKMuHFkw", group) 116 assert.Equal(t, "", task) 117 }) 118 t.Run("CheckRun without task", func(t *testing.T) { 119 root, group, task := tc.ParseTaskclusterURL("https://tc.community.com/tasks/groups/IWlO7NuxRnO0_8PKMuHFkw") 120 assert.Equal(t, "https://tc.community.com", root) 121 assert.Equal(t, "IWlO7NuxRnO0_8PKMuHFkw", group) 122 assert.Equal(t, "", task) 123 }) 124 } 125 126 func TestExtractArtifactURLs_all_success_master(t *testing.T) { 127 group := &tc.TaskGroupInfo{Tasks: make([]tc.TaskInfo, 5)} 128 group.Tasks[0].Name = "wpt-firefox-nightly-testharness-1" 129 group.Tasks[1].Name = "wpt-firefox-nightly-testharness-2" 130 group.Tasks[2].Name = "wpt-chrome-dev-testharness-1" 131 group.Tasks[3].Name = "wpt-chrome-dev-reftest-1" 132 group.Tasks[4].Name = "wpt-chrome-dev-crashtest-1" 133 for i := 0; i < len(group.Tasks); i++ { 134 group.Tasks[i].State = "completed" 135 group.Tasks[i].TaskID = fmt.Sprint(i) 136 } 137 138 t.Run("All", func(t *testing.T) { 139 urls, err := tc.ExtractArtifactURLs("https://tc.example.com", shared.NewNilLogger(), group, "") 140 assert.Nil(t, err) 141 assert.Equal(t, map[string]tc.ArtifactURLs{ 142 "firefox-nightly": { 143 Results: []string{ 144 "https://tc.example.com/api/queue/v1/task/0/artifacts/public/results/wpt_report.json.gz", 145 "https://tc.example.com/api/queue/v1/task/1/artifacts/public/results/wpt_report.json.gz", 146 }, 147 Screenshots: []string{ 148 "https://tc.example.com/api/queue/v1/task/0/artifacts/public/results/wpt_screenshot.txt.gz", 149 "https://tc.example.com/api/queue/v1/task/1/artifacts/public/results/wpt_screenshot.txt.gz", 150 }, 151 }, 152 "chrome-dev": { 153 Results: []string{ 154 "https://tc.example.com/api/queue/v1/task/2/artifacts/public/results/wpt_report.json.gz", 155 "https://tc.example.com/api/queue/v1/task/3/artifacts/public/results/wpt_report.json.gz", 156 "https://tc.example.com/api/queue/v1/task/4/artifacts/public/results/wpt_report.json.gz", 157 }, 158 Screenshots: []string{ 159 "https://tc.example.com/api/queue/v1/task/2/artifacts/public/results/wpt_screenshot.txt.gz", 160 "https://tc.example.com/api/queue/v1/task/3/artifacts/public/results/wpt_screenshot.txt.gz", 161 "https://tc.example.com/api/queue/v1/task/4/artifacts/public/results/wpt_screenshot.txt.gz", 162 }, 163 }, 164 }, urls) 165 }) 166 167 t.Run("Filtered", func(t *testing.T) { 168 urls, err := tc.ExtractArtifactURLs("https://tc.example.com", shared.NewNilLogger(), group, "0") 169 assert.Nil(t, err) 170 assert.Equal(t, map[string]tc.ArtifactURLs{ 171 "firefox-nightly": { 172 Results: []string{ 173 "https://tc.example.com/api/queue/v1/task/0/artifacts/public/results/wpt_report.json.gz", 174 }, 175 Screenshots: []string{ 176 "https://tc.example.com/api/queue/v1/task/0/artifacts/public/results/wpt_screenshot.txt.gz", 177 }, 178 }, 179 }, urls) 180 }) 181 } 182 183 func TestExtractArtifactURLs_all_success_pr(t *testing.T) { 184 group := &tc.TaskGroupInfo{Tasks: make([]tc.TaskInfo, 3)} 185 group.Tasks[0].Name = "wpt-chrome-dev-results" 186 group.Tasks[1].Name = "wpt-chrome-dev-stability" // must be skipped 187 group.Tasks[2].Name = "wpt-chrome-dev-results-without-changes" 188 for i := 0; i < len(group.Tasks); i++ { 189 group.Tasks[i].State = "completed" 190 group.Tasks[i].TaskID = fmt.Sprint(i) 191 } 192 193 t.Run("All", func(t *testing.T) { 194 urls, err := tc.ExtractArtifactURLs("https://tc.example.com", shared.NewNilLogger(), group, "") 195 assert.Nil(t, err) 196 assert.Equal(t, map[string]tc.ArtifactURLs{ 197 "chrome-dev-pr_head": { 198 Results: []string{ 199 "https://tc.example.com/api/queue/v1/task/0/artifacts/public/results/wpt_report.json.gz", 200 }, 201 Screenshots: []string{ 202 "https://tc.example.com/api/queue/v1/task/0/artifacts/public/results/wpt_screenshot.txt.gz", 203 }, 204 }, 205 "chrome-dev-pr_base": { 206 Results: []string{ 207 "https://tc.example.com/api/queue/v1/task/2/artifacts/public/results/wpt_report.json.gz", 208 }, 209 Screenshots: []string{ 210 "https://tc.example.com/api/queue/v1/task/2/artifacts/public/results/wpt_screenshot.txt.gz", 211 }, 212 }, 213 }, urls) 214 }) 215 216 t.Run("Filtered", func(t *testing.T) { 217 urls, err := tc.ExtractArtifactURLs("https://tc.example.com", shared.NewNilLogger(), group, "2") 218 assert.Nil(t, err) 219 assert.Equal(t, map[string]tc.ArtifactURLs{ 220 "chrome-dev-pr_base": { 221 Results: []string{ 222 "https://tc.example.com/api/queue/v1/task/2/artifacts/public/results/wpt_report.json.gz", 223 }, 224 Screenshots: []string{ 225 "https://tc.example.com/api/queue/v1/task/2/artifacts/public/results/wpt_screenshot.txt.gz", 226 }, 227 }, 228 }, urls) 229 }) 230 } 231 232 func TestExtractArtifactURLs_with_failures(t *testing.T) { 233 group := &tc.TaskGroupInfo{Tasks: make([]tc.TaskInfo, 3)} 234 group.Tasks[0].State = "failed" 235 group.Tasks[0].TaskID = "foo" 236 group.Tasks[0].Name = "wpt-firefox-nightly-testharness-1" 237 group.Tasks[1].State = "completed" 238 group.Tasks[1].TaskID = "bar" 239 group.Tasks[1].Name = "wpt-firefox-nightly-testharness-2" 240 group.Tasks[2].State = "completed" 241 group.Tasks[2].TaskID = "baz" 242 group.Tasks[2].Name = "wpt-chrome-dev-testharness-1" 243 244 urls, err := tc.ExtractArtifactURLs("https://tc.example.com", shared.NewNilLogger(), group, "") 245 assert.Nil(t, err) 246 assert.Equal(t, 1, len(urls)) 247 assert.Contains(t, urls, "chrome-dev") 248 } 249 250 func TestCreateAllRuns_success(t *testing.T) { 251 var requested uint32 252 requested = 0 253 handler := func(w http.ResponseWriter, r *http.Request) { 254 atomic.AddUint32(&requested, 1) 255 w.Write([]byte("OK")) 256 } 257 server := httptest.NewServer(http.HandlerFunc(handler)) 258 defer server.Close() 259 serverURL, _ := url.Parse(server.URL) 260 sha := "abcdef1234abcdef1234abcdef1234abcdef1234" 261 262 mockC := gomock.NewController(t) 263 defer mockC.Finish() 264 aeAPI := sharedtest.NewMockAppEngineAPI(mockC) 265 aeAPI.EXPECT().GetVersionedHostname().AnyTimes().Return("localhost:8080") 266 aeAPI.EXPECT().GetHTTPClientWithTimeout(uc.UploadTimeout).AnyTimes().Return(server.Client()) 267 aeAPI.EXPECT().GetResultsUploadURL().AnyTimes().Return(serverURL) 268 269 t.Run("master", func(t *testing.T) { 270 err := tc.CreateAllRuns( 271 shared.NewNilLogger(), 272 aeAPI, 273 sha, 274 "username", 275 "password", 276 map[string]tc.ArtifactURLs{ 277 "safari-preview": {Results: []string{"1"}}, 278 "chrome-dev": {Results: []string{"1"}}, 279 "firefox-stable": {Results: []string{"1", "2"}}, 280 }, 281 []string{shared.MasterLabel, "user:person"}, 282 ) 283 assert.Nil(t, err) 284 assert.Equal(t, uint32(3), requested) 285 }) 286 287 requested = 0 288 t.Run("PR", func(t *testing.T) { 289 err := tc.CreateAllRuns( 290 shared.NewNilLogger(), 291 aeAPI, 292 sha, 293 "username", 294 "password", 295 map[string]tc.ArtifactURLs{ 296 "chrome-dev-pr_head": {Results: []string{"1"}}, 297 "chrome-dev-pr_base": {Results: []string{"1"}}, 298 "firefox-stable-pr_head": {Results: []string{"1"}}, 299 "firefox-stable-pr_base": {Results: []string{"1"}}, 300 }, 301 nil, 302 ) 303 assert.Nil(t, err) 304 assert.Equal(t, uint32(4), requested) 305 }) 306 } 307 308 func TestCreateAllRuns_one_error(t *testing.T) { 309 var requested uint32 310 requested = 0 311 handler := func(w http.ResponseWriter, r *http.Request) { 312 if atomic.CompareAndSwapUint32(&requested, 0, 1) { 313 w.Write([]byte("OK")) 314 } else if atomic.CompareAndSwapUint32(&requested, 1, 2) { 315 http.Error(w, "Not found", http.StatusNotFound) 316 } else { 317 assert.FailNow(t, "requested != 0 && requested != 1") 318 } 319 } 320 server := httptest.NewServer(http.HandlerFunc(handler)) 321 defer server.Close() 322 mockC := gomock.NewController(t) 323 defer mockC.Finish() 324 325 sha := "abcdef1234abcdef1234abcdef1234abcdef1234" 326 327 aeAPI := sharedtest.NewMockAppEngineAPI(mockC) 328 aeAPI.EXPECT().GetVersionedHostname().MinTimes(1).Return("localhost:8080") 329 aeAPI.EXPECT().GetHTTPClientWithTimeout(uc.UploadTimeout).Times(2).Return(server.Client()) 330 serverURL, _ := url.Parse(server.URL) 331 aeAPI.EXPECT().GetResultsUploadURL().AnyTimes().Return(serverURL) 332 333 err := tc.CreateAllRuns( 334 shared.NewNilLogger(), 335 aeAPI, 336 sha, 337 "username", 338 "password", 339 map[string]tc.ArtifactURLs{ 340 "chrome": {Results: []string{"1"}}, 341 "firefox": {Results: []string{"1", "2"}}, 342 }, 343 []string{shared.MasterLabel}, 344 ) 345 assert.NotNil(t, err) 346 assert.Equal(t, uint32(2), requested) 347 assert.Contains(t, err.Error(), "API error:") 348 assert.Contains(t, err.Error(), "404") 349 } 350 351 func TestCreateAllRuns_all_errors(t *testing.T) { 352 handler := func(w http.ResponseWriter, r *http.Request) { 353 time.Sleep(time.Second * 2) 354 } 355 server := httptest.NewServer(http.HandlerFunc(handler)) 356 defer server.Close() 357 mockC := gomock.NewController(t) 358 defer mockC.Finish() 359 360 sha := "abcdef1234abcdef1234abcdef1234abcdef1234" 361 362 aeAPI := sharedtest.NewMockAppEngineAPI(mockC) 363 aeAPI.EXPECT().GetVersionedHostname().MinTimes(1).Return("localhost:8080") 364 // Give a very short timeout (instead of the asked 1min) to make tests faster. 365 aeAPI.EXPECT().GetHTTPClientWithTimeout(uc.UploadTimeout).MinTimes(1).Return(&http.Client{Timeout: time.Microsecond}) 366 serverURL, _ := url.Parse(server.URL) 367 aeAPI.EXPECT().GetResultsUploadURL().AnyTimes().Return(serverURL) 368 369 err := tc.CreateAllRuns( 370 shared.NewNilLogger(), 371 aeAPI, 372 sha, 373 "username", 374 "password", 375 map[string]tc.ArtifactURLs{ 376 "chrome": {Results: []string{"1"}}, 377 "firefox": {Results: []string{"1", "2"}}, 378 }, 379 []string{shared.MasterLabel}, 380 ) 381 assert.NotNil(t, err) 382 assert.Equal(t, 2, strings.Count(err.Error(), "Client.Timeout")) 383 } 384 385 func TestCreateAllRuns_pr_labels_exclude_master(t *testing.T) { 386 handler := func(w http.ResponseWriter, r *http.Request) { 387 // We should not see a master label here, even though we 388 // specify one in the call to tc.CreateAllRuns. 389 defer r.Body.Close() 390 body, _ := io.ReadAll(r.Body) 391 assert.NotContains(t, string(body), "master") 392 w.Write([]byte("OK")) 393 } 394 server := httptest.NewServer(http.HandlerFunc(handler)) 395 defer server.Close() 396 serverURL, _ := url.Parse(server.URL) 397 sha := "abcdef1234abcdef1234abcdef1234abcdef1234" 398 399 mockC := gomock.NewController(t) 400 defer mockC.Finish() 401 aeAPI := sharedtest.NewMockAppEngineAPI(mockC) 402 aeAPI.EXPECT().GetVersionedHostname().AnyTimes().Return("localhost:8080") 403 aeAPI.EXPECT().GetHTTPClientWithTimeout(uc.UploadTimeout).AnyTimes().Return(server.Client()) 404 aeAPI.EXPECT().GetResultsUploadURL().AnyTimes().Return(serverURL) 405 406 // This test reproduces the case where Community-TC executes a pull 407 // request run on a master commit (which we have historically seen). 408 // When we get a master-tagged run which contains pull-request runs, we 409 // should ignore the tag. This is asserted by the HTTP handler above. 410 err := tc.CreateAllRuns( 411 shared.NewNilLogger(), 412 aeAPI, 413 sha, 414 "username", 415 "password", 416 map[string]tc.ArtifactURLs{ 417 "chrome-dev-pr_head": {Results: []string{"1"}}, 418 "chrome-dev-pr_base": {Results: []string{"1"}}, 419 "firefox-stable-pr_head": {Results: []string{"1"}}, 420 "firefox-stable-pr_base": {Results: []string{"1"}}, 421 }, 422 []string{shared.MasterLabel, "user:person"}, 423 ) 424 assert.Nil(t, err) 425 } 426 427 func TestTaskNameRegex(t *testing.T) { 428 assert.Equal(t, []string{"chrome-dev", "results"}, tc.TaskNameRegex.FindStringSubmatch("wpt-chrome-dev-results")[1:]) 429 assert.Equal(t, []string{"chrome-dev", "results-without-changes"}, tc.TaskNameRegex.FindStringSubmatch("wpt-chrome-dev-results-without-changes")[1:]) 430 assert.Equal(t, []string{"chrome-dev", "stability"}, tc.TaskNameRegex.FindStringSubmatch("wpt-chrome-dev-stability")[1:]) 431 assert.Equal(t, []string{"chrome-stable", "reftest"}, tc.TaskNameRegex.FindStringSubmatch("wpt-chrome-stable-reftest-1")[1:]) 432 assert.Equal(t, []string{"firefox-beta", "crashtest"}, tc.TaskNameRegex.FindStringSubmatch("wpt-firefox-beta-crashtest-2")[1:]) 433 assert.Equal(t, []string{"firefox-nightly", "testharness"}, tc.TaskNameRegex.FindStringSubmatch("wpt-firefox-nightly-testharness-5")[1:]) 434 assert.Equal(t, []string{"firefox-stable", "wdspec"}, tc.TaskNameRegex.FindStringSubmatch("wpt-firefox-stable-wdspec-1")[1:]) 435 assert.Equal(t, []string{"webkitgtk_minibrowser-nightly", "testharness"}, tc.TaskNameRegex.FindStringSubmatch("wpt-webkitgtk_minibrowser-nightly-testharness-2")[1:]) 436 assert.Nil(t, tc.TaskNameRegex.FindStringSubmatch("wpt-foo-bar--1")) 437 assert.Nil(t, tc.TaskNameRegex.FindStringSubmatch("wpt-foo-bar-")) 438 } 439 440 func TestGetStatusEventInfo_target_url(t *testing.T) { 441 mockC := gomock.NewController(t) 442 defer mockC.Finish() 443 api := mock_tc.NewMockAPI(mockC) 444 api.EXPECT().GetTaskGroupInfo("https://tc.community.com", "IWlO7NuxRnO0_8PKMuHFkw").Return(nil, nil) 445 446 status := tc.StatusEventPayload{} 447 status.State = strPtr("success") 448 status.TargetURL = strPtr("https://tc.community.com/tasks/groups/IWlO7NuxRnO0_8PKMuHFkw/tasks/123") 449 status.Context = strPtr("Community-TC") 450 status.Branches = branchInfos{&github.Branch{Name: strPtr(shared.MasterLabel)}} 451 status.SHA = strPtr("abcdef123") 452 453 // The target URL must be present, and must at least be a recognized 454 // URL containing a taskGroupID. ParseTaskclusterURL is tested 455 // separately, so just do a basic check here. 456 event, err := tc.GetStatusEventInfo(status, shared.NewNilLogger(), api) 457 assert.Equal(t, event.RootURL, "https://tc.community.com") 458 assert.Equal(t, event.TaskID, "123") 459 assert.Nil(t, err) 460 461 status.TargetURL = strPtr("https://example.com/nope/not/right") 462 event, err = tc.GetStatusEventInfo(status, shared.NewNilLogger(), api) 463 assert.NotNil(t, err) 464 465 status.TargetURL = nil 466 event, err = tc.GetStatusEventInfo(status, shared.NewNilLogger(), api) 467 assert.NotNil(t, err) 468 } 469 470 func TestGetStatusEventInfo_sha(t *testing.T) { 471 mockC := gomock.NewController(t) 472 defer mockC.Finish() 473 api := mock_tc.NewMockAPI(mockC) 474 api.EXPECT().GetTaskGroupInfo(gomock.Any(), gomock.Any()).Return(nil, nil) 475 476 status := tc.StatusEventPayload{} 477 status.State = strPtr("success") 478 status.TargetURL = strPtr("https://tc.community.com/tasks/groups/IWlO7NuxRnO0_8PKMuHFkw/tasks/123") 479 status.Context = strPtr("Community-TC") 480 status.Branches = branchInfos{&github.Branch{Name: strPtr(shared.MasterLabel)}} 481 status.SHA = strPtr("abcdef123") 482 483 // We don't place requirements on the SHA other than it exists. 484 event, err := tc.GetStatusEventInfo(status, shared.NewNilLogger(), api) 485 assert.Equal(t, event.Sha, "abcdef123") 486 assert.Nil(t, err) 487 488 status.SHA = nil 489 event, err = tc.GetStatusEventInfo(status, shared.NewNilLogger(), api) 490 assert.NotNil(t, err) 491 } 492 493 func TestGetStatusEventInfo_master(t *testing.T) { 494 mockC := gomock.NewController(t) 495 defer mockC.Finish() 496 api := mock_tc.NewMockAPI(mockC) 497 api.EXPECT().GetTaskGroupInfo(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() 498 499 status := tc.StatusEventPayload{} 500 status.State = strPtr("success") 501 status.TargetURL = strPtr("https://tc.community.com/tasks/groups/IWlO7NuxRnO0_8PKMuHFkw/tasks/123") 502 status.Context = strPtr("Community-TC") 503 status.Branches = branchInfos{&github.Branch{Name: strPtr("mybranch")}, &github.Branch{Name: strPtr(shared.MasterLabel)}} 504 status.SHA = strPtr("abcdef123") 505 506 // We check whether an event is for master by looking at the branches 507 // it is associated with. 508 event, err := tc.GetStatusEventInfo(status, shared.NewNilLogger(), api) 509 assert.Equal(t, event.Master, true) 510 assert.Nil(t, err) 511 512 status.Branches = branchInfos{&github.Branch{Name: strPtr("mybranch")}} 513 event, err = tc.GetStatusEventInfo(status, shared.NewNilLogger(), api) 514 assert.Equal(t, event.Master, false) 515 assert.Nil(t, err) 516 517 // Missing the 'branches' entry is not an error; the event just isn't 518 // for master. 519 status.Branches = nil 520 event, err = tc.GetStatusEventInfo(status, shared.NewNilLogger(), api) 521 assert.Equal(t, event.Master, false) 522 assert.Nil(t, err) 523 } 524 525 func TestGetStatusEventInfo_sender(t *testing.T) { 526 mockC := gomock.NewController(t) 527 defer mockC.Finish() 528 api := mock_tc.NewMockAPI(mockC) 529 api.EXPECT().GetTaskGroupInfo(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() 530 531 status := tc.StatusEventPayload{} 532 status.State = strPtr("success") 533 status.TargetURL = strPtr("https://tc.community.com/tasks/groups/IWlO7NuxRnO0_8PKMuHFkw/tasks/123") 534 status.Context = strPtr("Community-TC") 535 status.Branches = branchInfos{&github.Branch{Name: strPtr(shared.MasterLabel)}} 536 status.SHA = strPtr("abcdef123") 537 538 // The sender is entirely optional. 539 status.Commit = &github.RepositoryCommit{Author: &github.User{Login: strPtr("someuser")}} 540 event, err := tc.GetStatusEventInfo(status, shared.NewNilLogger(), api) 541 assert.Equal(t, event.Sender, "someuser") 542 assert.Nil(t, err) 543 544 status.Commit = nil 545 event, err = tc.GetStatusEventInfo(status, shared.NewNilLogger(), api) 546 assert.Equal(t, event.Sender, "") 547 assert.Nil(t, err) 548 } 549 550 func TestGetStatusEventInfo_group(t *testing.T) { 551 mockC := gomock.NewController(t) 552 defer mockC.Finish() 553 api := mock_tc.NewMockAPI(mockC) 554 group := &tc.TaskGroupInfo{Tasks: make([]tc.TaskInfo, 0)} 555 556 status := tc.StatusEventPayload{} 557 status.State = strPtr("success") 558 status.TargetURL = strPtr("https://tc.community.com/tasks/groups/IWlO7NuxRnO0_8PKMuHFkw/tasks/123") 559 status.Context = strPtr("Community-TC") 560 status.Branches = branchInfos{&github.Branch{Name: strPtr(shared.MasterLabel)}} 561 status.SHA = strPtr("abcdef123") 562 563 api.EXPECT().GetTaskGroupInfo(gomock.Any(), gomock.Any()).Return(group, nil).Times(1) 564 event, err := tc.GetStatusEventInfo(status, shared.NewNilLogger(), api) 565 assert.Equal(t, event.Group, group) 566 assert.Nil(t, err) 567 568 api.EXPECT().GetTaskGroupInfo(gomock.Any(), gomock.Any()).Return(nil, errors.New("failed")).Times(1) 569 event, err = tc.GetStatusEventInfo(status, shared.NewNilLogger(), api) 570 assert.NotNil(t, err) 571 } 572 573 func TestGetCheckSuiteEventInfo_sourceRepo(t *testing.T) { 574 mockC := gomock.NewController(t) 575 defer mockC.Finish() 576 api := mock_tc.NewMockAPI(mockC) 577 578 runs := []*github.CheckRun{ 579 { 580 Name: strPtr("wpt-decision-task"), 581 Status: strPtr("completed"), 582 DetailsURL: strPtr("https://community-tc.services.mozilla.com/tasks/Jq4HzLz0R2eKkJFdmf47Bg"), 583 }, 584 } 585 api.EXPECT().ListCheckRuns(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(runs, nil) 586 587 event := github.CheckSuiteEvent{ 588 CheckSuite: &github.CheckSuite{ 589 HeadSHA: strPtr("abcdef123"), 590 }, 591 Repo: &github.Repository{ 592 Owner: &github.User{ 593 Login: strPtr("web-platform-tests"), 594 }, 595 Name: strPtr("wpt"), 596 }, 597 } 598 599 // Valid owner and name. 600 _, err := tc.GetCheckSuiteEventInfo(event, shared.NewNilLogger(), api) 601 assert.Nil(t, err) 602 603 // Invalid name. 604 event.Repo.Name = strPtr("not-wpt") 605 _, err = tc.GetCheckSuiteEventInfo(event, shared.NewNilLogger(), api) 606 assert.NotNil(t, err) 607 608 // Invalid owner. 609 event.Repo.Name = strPtr("wpt") 610 event.Repo.Owner.Login = strPtr("stephenmcgruer") 611 _, err = tc.GetCheckSuiteEventInfo(event, shared.NewNilLogger(), api) 612 assert.NotNil(t, err) 613 } 614 615 func TestGetCheckSuiteEventInfo_sha(t *testing.T) { 616 mockC := gomock.NewController(t) 617 defer mockC.Finish() 618 api := mock_tc.NewMockAPI(mockC) 619 620 runs := []*github.CheckRun{ 621 { 622 Name: strPtr("wpt-decision-task"), 623 Status: strPtr("completed"), 624 DetailsURL: strPtr("https://community-tc.services.mozilla.com/tasks/Jq4HzLz0R2eKkJFdmf47Bg"), 625 }, 626 } 627 api.EXPECT().ListCheckRuns(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(runs, nil) 628 629 event := github.CheckSuiteEvent{ 630 CheckSuite: &github.CheckSuite{ 631 HeadSHA: strPtr("abcdef123"), 632 }, 633 Repo: &github.Repository{ 634 Owner: &github.User{ 635 Login: strPtr("web-platform-tests"), 636 }, 637 Name: strPtr("wpt"), 638 }, 639 } 640 641 // We don't place requirements on the SHA other than it exists. 642 eventInfo, err := tc.GetCheckSuiteEventInfo(event, shared.NewNilLogger(), api) 643 assert.Equal(t, "abcdef123", eventInfo.Sha) 644 assert.Nil(t, err) 645 646 event.CheckSuite.HeadSHA = nil 647 eventInfo, err = tc.GetCheckSuiteEventInfo(event, shared.NewNilLogger(), api) 648 assert.NotNil(t, err) 649 } 650 651 func TestGetCheckSuiteEventInfo_master(t *testing.T) { 652 mockC := gomock.NewController(t) 653 defer mockC.Finish() 654 api := mock_tc.NewMockAPI(mockC) 655 656 runs := []*github.CheckRun{ 657 { 658 Name: strPtr("wpt-decision-task"), 659 Status: strPtr("completed"), 660 DetailsURL: strPtr("https://community-tc.services.mozilla.com/tasks/Jq4HzLz0R2eKkJFdmf47Bg"), 661 }, 662 } 663 api.EXPECT().ListCheckRuns(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(runs, nil) 664 665 event := github.CheckSuiteEvent{ 666 CheckSuite: &github.CheckSuite{ 667 HeadBranch: strPtr("master"), 668 HeadSHA: strPtr("abcdef123"), 669 }, 670 Repo: &github.Repository{ 671 Owner: &github.User{ 672 Login: strPtr("web-platform-tests"), 673 }, 674 Name: strPtr("wpt"), 675 }, 676 } 677 678 eventInfo, err := tc.GetCheckSuiteEventInfo(event, shared.NewNilLogger(), api) 679 assert.Equal(t, true, eventInfo.Master) 680 assert.Nil(t, err) 681 682 event.CheckSuite.HeadBranch = strPtr("my-branch") 683 eventInfo, err = tc.GetCheckSuiteEventInfo(event, shared.NewNilLogger(), api) 684 assert.Equal(t, false, eventInfo.Master) 685 assert.Nil(t, err) 686 } 687 688 func TestGetCheckSuiteEventInfo_sender(t *testing.T) { 689 mockC := gomock.NewController(t) 690 defer mockC.Finish() 691 api := mock_tc.NewMockAPI(mockC) 692 693 runs := []*github.CheckRun{ 694 { 695 Name: strPtr("wpt-decision-task"), 696 Status: strPtr("completed"), 697 DetailsURL: strPtr("https://community-tc.services.mozilla.com/tasks/Jq4HzLz0R2eKkJFdmf47Bg"), 698 }, 699 } 700 api.EXPECT().ListCheckRuns(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(runs, nil) 701 702 event := github.CheckSuiteEvent{ 703 Sender: &github.User{ 704 Login: strPtr("myuser"), 705 }, 706 CheckSuite: &github.CheckSuite{ 707 HeadSHA: strPtr("abcdef123"), 708 }, 709 Repo: &github.Repository{ 710 Owner: &github.User{ 711 Login: strPtr("web-platform-tests"), 712 }, 713 Name: strPtr("wpt"), 714 }, 715 } 716 717 eventInfo, err := tc.GetCheckSuiteEventInfo(event, shared.NewNilLogger(), api) 718 assert.Equal(t, "myuser", eventInfo.Sender) 719 assert.Nil(t, err) 720 721 event.Sender.Login = nil 722 eventInfo, err = tc.GetCheckSuiteEventInfo(event, shared.NewNilLogger(), api) 723 assert.Equal(t, "", eventInfo.Sender) 724 assert.Nil(t, err) 725 } 726 727 func TestGetCheckSuiteEventInfo_checkRuns(t *testing.T) { 728 mockC := gomock.NewController(t) 729 defer mockC.Finish() 730 api := mock_tc.NewMockAPI(mockC) 731 732 // The list of check_run events give us two main pieces of information: 733 // 734 // the RootURL, which must match across runs, and 735 // the TaskGroupInfo: 736 // TaskGroupID is the wpt-decision-tasks's taskID 737 // Tasks is filled with each check_run's name, taskID, and status. 738 runs := []*github.CheckRun{ 739 { 740 Name: strPtr("wpt-decision-task"), 741 Status: strPtr("completed"), 742 Conclusion: strPtr("success"), 743 DetailsURL: strPtr("https://community-tc.services.mozilla.com/tasks/Jq4HzLz0R2eKkJFdmf47Bg"), 744 }, 745 { 746 Name: strPtr("wpt-chrome-dev-testharness-1"), 747 Status: strPtr("completed"), 748 Conclusion: strPtr("failed"), 749 DetailsURL: strPtr("https://community-tc.services.mozilla.com/tasks/IWlO7NuxRnO0_8PKMuHFkw"), 750 }, 751 } 752 api.EXPECT().ListCheckRuns(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(runs, nil) 753 754 event := github.CheckSuiteEvent{ 755 CheckSuite: &github.CheckSuite{ 756 HeadSHA: strPtr("abcdef123"), 757 }, 758 Repo: &github.Repository{ 759 Owner: &github.User{ 760 Login: strPtr("web-platform-tests"), 761 }, 762 Name: strPtr("wpt"), 763 }, 764 } 765 766 eventInfo, err := tc.GetCheckSuiteEventInfo(event, shared.NewNilLogger(), api) 767 assert.Equal(t, "https://community-tc.services.mozilla.com", eventInfo.RootURL) 768 assert.Equal(t, "Jq4HzLz0R2eKkJFdmf47Bg", eventInfo.Group.TaskGroupID) 769 assert.Equal(t, "wpt-decision-task", eventInfo.Group.Tasks[0].Name) 770 assert.Equal(t, "Jq4HzLz0R2eKkJFdmf47Bg", eventInfo.Group.Tasks[0].TaskID) 771 assert.Equal(t, "completed", eventInfo.Group.Tasks[0].State) 772 assert.Equal(t, "wpt-chrome-dev-testharness-1", eventInfo.Group.Tasks[1].Name) 773 assert.Equal(t, "IWlO7NuxRnO0_8PKMuHFkw", eventInfo.Group.Tasks[1].TaskID) 774 assert.Equal(t, "failed", eventInfo.Group.Tasks[1].State) 775 assert.Nil(t, err) 776 777 // Check the case where a details URL will fail to parse. 778 runs[0].DetailsURL = strPtr("https://example.com/nope/not/right") 779 eventInfo, err = tc.GetCheckSuiteEventInfo(event, shared.NewNilLogger(), api) 780 assert.NotNil(t, err) 781 782 // Check the case where a details URL is missing. 783 runs[0].DetailsURL = nil 784 eventInfo, err = tc.GetCheckSuiteEventInfo(event, shared.NewNilLogger(), api) 785 assert.NotNil(t, err) 786 787 // Check the case where a details URL has a mismatching root URL. 788 runs[0].DetailsURL = strPtr("https://tc.community.com/tasks/Jq4HzLz0R2eKkJFdmf47Bg") 789 eventInfo, err = tc.GetCheckSuiteEventInfo(event, shared.NewNilLogger(), api) 790 assert.NotNil(t, err) 791 } 792 793 func TestGetCheckSuiteEventInfo_checkRunsEmpty(t *testing.T) { 794 mockC := gomock.NewController(t) 795 defer mockC.Finish() 796 api := mock_tc.NewMockAPI(mockC) 797 api.EXPECT().ListCheckRuns(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return([]*github.CheckRun{}, nil) 798 799 event := github.CheckSuiteEvent{ 800 CheckSuite: &github.CheckSuite{ 801 HeadSHA: strPtr("abcdef123"), 802 }, 803 Repo: &github.Repository{ 804 Owner: &github.User{ 805 Login: strPtr("web-platform-tests"), 806 }, 807 Name: strPtr("wpt"), 808 }, 809 } 810 811 _, err := tc.GetCheckSuiteEventInfo(event, shared.NewNilLogger(), api) 812 assert.NotNil(t, err) 813 } 814 815 func TestGetCheckSuiteEventInfo_checkRunsFailed(t *testing.T) { 816 mockC := gomock.NewController(t) 817 defer mockC.Finish() 818 api := mock_tc.NewMockAPI(mockC) 819 api.EXPECT().ListCheckRuns(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(nil, errors.New("failed")) 820 821 event := github.CheckSuiteEvent{ 822 CheckSuite: &github.CheckSuite{ 823 HeadSHA: strPtr("abcdef123"), 824 }, 825 Repo: &github.Repository{ 826 Owner: &github.User{ 827 Login: strPtr("web-platform-tests"), 828 }, 829 Name: strPtr("wpt"), 830 }, 831 } 832 833 _, err := tc.GetCheckSuiteEventInfo(event, shared.NewNilLogger(), api) 834 assert.NotNil(t, err) 835 }