예제 #1
0
func TestMungePullRequest(t *testing.T) {
	runtime.GOMAXPROCS(runtime.NumCPU())

	tests := []struct {
		pr               *github.PullRequest
		issue            *github.Issue
		commits          []github.RepositoryCommit
		events           []github.IssueEvent
		ciStatus         github.CombinedStatus
		jenkinsJob       jenkins.Job
		shouldWaitForE2E bool
		githubE2EPass    bool
		shouldPass       bool
		reason           string
	}{
		// Should pass because the entire thing was run and good
		{
			pr:               ValidPR(),
			issue:            NoOKToMergeIssue(),
			events:           NewLGTMEvents(),
			commits:          Commits(),
			ciStatus:         SuccessStatus(),
			jenkinsJob:       SuccessJenkins(),
			shouldWaitForE2E: true,
			githubE2EPass:    true,
			shouldPass:       true,
			reason:           merged,
		},
		// Should merge even though github ci failed because of dont-require-e2e
		{
			pr:         ValidPR(),
			issue:      DontRequireGithubE2EIssue(),
			ciStatus:   GitHubE2EFailStatus(),
			events:     NewLGTMEvents(),
			commits:    Commits(),
			jenkinsJob: SuccessJenkins(),
			reason:     merged,
		},
		// Fail because PR can't automatically merge
		{
			pr:     UnMergeablePR(),
			issue:  NoOKToMergeIssue(),
			reason: unmergeable,
		},
		// Fail because we don't know if PR can automatically merge
		{
			pr:     UndeterminedMergeablePR(),
			issue:  NoOKToMergeIssue(),
			reason: undeterminedMergability,
		},
		// Fail because the "cla: yes" label was not applied
		{
			pr:     ValidPR(),
			issue:  NoCLAIssue(),
			reason: noCLA,
		},
		// Fail because github CI tests have failed (or at least are not success)
		{
			pr:     NonWhitelistUserPR(),
			issue:  NoOKToMergeIssue(),
			reason: ciFailure,
		},
		// Fail because the use is not in the whitelist and we don't have "ok-to-merge"
		{
			pr:       NonWhitelistUserPR(),
			issue:    NoOKToMergeIssue(),
			ciStatus: SuccessStatus(),
			reason:   needsok,
		},
		// Fail because missing LGTM label
		{
			pr:       NonWhitelistUserPR(),
			issue:    NoLGTMIssue(),
			ciStatus: SuccessStatus(),
			reason:   noLGTM,
		},
		// Fail because we can't tell if LGTM was added before the last change
		{
			pr:       ValidPR(),
			issue:    NoOKToMergeIssue(),
			ciStatus: SuccessStatus(),
			reason:   unknown,
		},
		// Fail because LGTM was added before the last change
		{
			pr:       ValidPR(),
			issue:    AllLabelsIssue(),
			ciStatus: SuccessStatus(),
			events:   OldLGTMEvents(),
			commits:  Commits(),
			reason:   lgtmEarly,
		},
		// Fail because jenkins instances are failing (whole submit queue blocks)
		{
			pr:         ValidPR(),
			issue:      NoOKToMergeIssue(),
			ciStatus:   SuccessStatus(),
			events:     NewLGTMEvents(),
			commits:    Commits(),
			jenkinsJob: FailJenkins(),
			reason:     e2eFailure,
		},
		// This is not really a failure, we just check that it reports e2e is in progress
		{
			pr:            ValidPR(),
			issue:         AllLabelsIssue(),
			ciStatus:      SuccessStatus(),
			events:        NewLGTMEvents(),
			commits:       Commits(),
			jenkinsJob:    SuccessJenkins(),
			githubE2EPass: false,
			reason:        githube2e,
		},
		// Fail because the second run of github e2e tests failed
		{
			pr:               ValidPR(),
			issue:            AllLabelsIssue(),
			ciStatus:         SuccessStatus(),
			events:           NewLGTMEvents(),
			commits:          Commits(),
			jenkinsJob:       SuccessJenkins(),
			githubE2EPass:    false,
			shouldWaitForE2E: true,
			reason:           githube2efail,
		},
	}
	for testNum, test := range tests {
		client, server, mux := github_test.InitTest()

		config := &github_util.Config{}
		config.Org = "o"
		config.Project = "r"
		config.SetClient(client)
		// Don't wait so long for it to go pending or back
		d := 250 * time.Millisecond
		config.PendingWaitTime = &d

		// Respond with success to jenkins requests.
		mux.HandleFunc("/job/foo/lastCompletedBuild/api/json", func(w http.ResponseWriter, r *http.Request) {
			if r.Method != "GET" {
				t.Errorf("Unexpected method: %s", r.Method)
			}
			w.WriteHeader(http.StatusOK)
			data, err := json.Marshal(test.jenkinsJob)
			if err != nil {
				t.Errorf("Unexpected error: %v", err)
			}
			w.Write(data)
		})
		mux.HandleFunc("/repos/o/r/commits/mysha/status", func(w http.ResponseWriter, r *http.Request) {
			if r.Method != "GET" {
				t.Errorf("Unexpected method: %s", r.Method)
			}
			w.WriteHeader(http.StatusOK)
			data, err := json.Marshal(test.ciStatus)
			if err != nil {
				t.Errorf("Unexpected error: %v", err)
			}
			w.Write(data)
		})
		mux.HandleFunc("/repos/o/r/issues/1/comments", func(w http.ResponseWriter, r *http.Request) {
			if r.Method != "POST" {
				t.Errorf("Unexpected method: %s", r.Method)
			}

			type comment struct {
				Body string `json:"body"`
			}
			c := new(comment)
			json.NewDecoder(r.Body).Decode(c)
			msg := c.Body
			if strings.HasPrefix(msg, "@k8s-bot test this") {
				go fakeRunGithubE2ESuccess(&test.ciStatus, test.shouldPass)
			}
			w.WriteHeader(http.StatusOK)
			data, err := json.Marshal(github.IssueComment{})
			if err != nil {
				t.Errorf("Unexpected error: %v", err)
			}
			w.Write(data)
		})
		mux.HandleFunc("/repos/o/r/pulls/1/merge", func(w http.ResponseWriter, r *http.Request) {
			if r.Method != "PUT" {
				t.Errorf("Unexpected method: %s", r.Method)
			}
			w.WriteHeader(http.StatusOK)
			data, err := json.Marshal(github.PullRequestMergeResult{})
			if err != nil {
				t.Errorf("Unexpected error: %v", err)
			}
			w.Write(data)
			test.pr.Merged = boolPtr(true)
		})

		sq := SubmitQueue{}
		sq.RequiredStatusContexts = []string{"cla/google"}
		sq.DontRequireE2ELabel = "e2e-not-required"
		sq.JenkinsHost = server.URL
		sq.JenkinsJobs = []string{"foo"}
		sq.WhitelistOverride = "ok-to-merge"
		sq.Initialize(config)
		sq.EachLoop(config)
		sq.userWhitelist.Insert("k8s-merge-robot")
		sq.DontRequireE2ELabel = "e2e-not-required"

		sq.MungePullRequest(config, test.pr, test.issue, test.commits, test.events)
		if test.shouldWaitForE2E {
			// probably should hold the lock when I check, but what a PITA
			for len(sq.needsGithubE2E) == 0 {
				time.Sleep(1 * time.Millisecond)
			}
			for len(sq.needsGithubE2E) != 0 {
				time.Sleep(1 * time.Millisecond)
			}
		}
		if sq.prStatus["1"].Reason != test.reason {
			t.Errorf("test:%d expected reason=%q but got %q", testNum, test.reason, sq.prStatus["1"].Reason)
		}
		server.Close()
	}
}
예제 #2
0
func TestGetLastModified(t *testing.T) {
	tests := []struct {
		commits      []github.RepositoryCommit
		expectedTime *time.Time
	}{
		{
			commits: []github.RepositoryCommit{
				{
					Commit: &github.Commit{
						Committer: &github.CommitAuthor{
							Date: timePtr(time.Unix(10, 0)),
						},
					},
				},
			},
			expectedTime: timePtr(time.Unix(10, 0)),
		},
		{
			commits: []github.RepositoryCommit{
				{
					Commit: &github.Commit{
						Committer: &github.CommitAuthor{
							Date: timePtr(time.Unix(10, 0)),
						},
					},
				},
				{
					Commit: &github.Commit{
						Committer: &github.CommitAuthor{
							Date: timePtr(time.Unix(11, 0)),
						},
					},
				},
				{
					Commit: &github.Commit{
						Committer: &github.CommitAuthor{
							Date: timePtr(time.Unix(12, 0)),
						},
					},
				},
			},
			expectedTime: timePtr(time.Unix(12, 0)),
		},
		{
			commits: []github.RepositoryCommit{
				{
					Commit: &github.Commit{
						Committer: &github.CommitAuthor{
							Date: timePtr(time.Unix(10, 0)),
						},
					},
				},
				{
					Commit: &github.Commit{
						Committer: &github.CommitAuthor{
							Date: timePtr(time.Unix(9, 0)),
						},
					},
				},
				{
					Commit: &github.Commit{
						Committer: &github.CommitAuthor{
							Date: timePtr(time.Unix(8, 0)),
						},
					},
				},
			},
			expectedTime: timePtr(time.Unix(10, 0)),
		},
		{
			commits: []github.RepositoryCommit{
				{
					Commit: &github.Commit{
						Committer: &github.CommitAuthor{
							Date: timePtr(time.Unix(9, 0)),
						},
					},
				},
				{
					Commit: &github.Commit{
						Committer: &github.CommitAuthor{
							Date: timePtr(time.Unix(10, 0)),
						},
					},
				},
				{
					Commit: &github.Commit{
						Committer: &github.CommitAuthor{
							Date: timePtr(time.Unix(9, 0)),
						},
					},
				},
			},
			expectedTime: timePtr(time.Unix(10, 0)),
		},
	}
	for _, test := range tests {
		client, server, mux := github_test.InitTest()
		config := &Config{
			client:  client,
			Org:     "o",
			Project: "r",
		}
		mux.HandleFunc(fmt.Sprintf("/repos/o/r/pulls/1/commits"), func(w http.ResponseWriter, r *http.Request) {
			if r.Method != "GET" {
				t.Errorf("Unexpected method: %s", r.Method)
			}
			w.WriteHeader(http.StatusOK)
			data, err := json.Marshal(test.commits)
			if err != nil {
				t.Errorf("Unexpected error: %v", err)
			}
			w.Write(data)
			ts, err := config.LastModifiedTime(1)
			if err != nil {
				t.Errorf("unexpected error: %v", err)
			}
			if !ts.Equal(*test.expectedTime) {
				t.Errorf("expected: %v, saw: %v", test.expectedTime, ts)
			}
		})
		server.Close()
	}
}
예제 #3
0
func TestValidateLGTMAfterPush(t *testing.T) {
	tests := []struct {
		issueEvents  []github.IssueEvent
		shouldPass   bool
		lastModified time.Time
	}{
		{
			issueEvents: []github.IssueEvent{
				{
					Event: stringPtr("labeled"),
					Label: &github.Label{
						Name: stringPtr("lgtm"),
					},
					CreatedAt: timePtr(time.Unix(10, 0)),
				},
			},
			lastModified: time.Unix(9, 0),
			shouldPass:   true,
		},
		{
			issueEvents: []github.IssueEvent{
				{
					Event: stringPtr("labeled"),
					Label: &github.Label{
						Name: stringPtr("lgtm"),
					},
					CreatedAt: timePtr(time.Unix(10, 0)),
				},
			},
			lastModified: time.Unix(11, 0),
			shouldPass:   false,
		},
		{
			issueEvents: []github.IssueEvent{
				{
					Event: stringPtr("labeled"),
					Label: &github.Label{
						Name: stringPtr("lgtm"),
					},
					CreatedAt: timePtr(time.Unix(12, 0)),
				},
				{
					Event: stringPtr("labeled"),
					Label: &github.Label{
						Name: stringPtr("lgtm"),
					},
					CreatedAt: timePtr(time.Unix(11, 0)),
				},
				{
					Event: stringPtr("labeled"),
					Label: &github.Label{
						Name: stringPtr("lgtm"),
					},
					CreatedAt: timePtr(time.Unix(10, 0)),
				},
			},
			lastModified: time.Unix(11, 0),
			shouldPass:   true,
		},
		{
			issueEvents: []github.IssueEvent{
				{
					Event: stringPtr("labeled"),
					Label: &github.Label{
						Name: stringPtr("lgtm"),
					},
					CreatedAt: timePtr(time.Unix(10, 0)),
				},
				{
					Event: stringPtr("labeled"),
					Label: &github.Label{
						Name: stringPtr("lgtm"),
					},
					CreatedAt: timePtr(time.Unix(11, 0)),
				},
				{
					Event: stringPtr("labeled"),
					Label: &github.Label{
						Name: stringPtr("lgtm"),
					},
					CreatedAt: timePtr(time.Unix(12, 0)),
				},
			},
			lastModified: time.Unix(11, 0),
			shouldPass:   true,
		},
	}
	for _, test := range tests {
		config := &github_util.Config{}
		client, server, mux := github_test.InitTest()
		config.Org = "o"
		config.Project = "r"
		config.SetClient(client)

		mux.HandleFunc(fmt.Sprintf("/repos/o/r/issues/1/events"), func(w http.ResponseWriter, r *http.Request) {
			if r.Method != "GET" {
				t.Errorf("Unexpected method: %s", r.Method)
			}
			w.WriteHeader(http.StatusOK)
			data, err := json.Marshal(test.issueEvents)
			if err != nil {
				t.Errorf("Unexpected error: %v", err)
			}
			w.Write(data)

			commits, err := config.GetFilledCommits(1)
			if err != nil {
				t.Errorf("Unexpected error getting filled commits: %v", err)
			}

			events, err := config.GetAllEventsForPR(1)
			if err != nil {
				t.Errorf("Unexpected error getting events commits: %v", err)
			}
			lastModifiedTime := github_util.LastModifiedTime(commits)
			lgtmTime := github_util.LabelTime("lgtm", events)

			if lastModifiedTime == nil || lgtmTime == nil {
				t.Errorf("unexpected lastModifiedTime or lgtmTime == nil")
			}

			ok := !lastModifiedTime.After(*lgtmTime)

			if ok != test.shouldPass {
				t.Errorf("expected: %v, saw: %v", test.shouldPass, ok)
			}
		})
		server.Close()
	}
}
예제 #4
0
func TestForEachPRDo(t *testing.T) {
	prlinks := github.PullRequestLinks{}
	user := github.User{Login: stringp("bob")}
	tests := []struct {
		Issues   [][]github.Issue
		PRs      map[int]github.PullRequest
		Pages    []int
		ValidPRs int
	}{
		{
			Issues: [][]github.Issue{
				{
					{
						PullRequestLinks: &prlinks,
						Number:           intp(1),
						User:             &user,
					},
				},
			},
			PRs: map[int]github.PullRequest{
				1: PR(1, false),
			},
			Pages:    []int{0},
			ValidPRs: 1,
		},
		{
			Issues: [][]github.Issue{
				{
					{
						Number: intp(1),
						User:   &user,
					},
				},
				{
					{
						PullRequestLinks: &prlinks,
						Number:           intp(2),
						User:             &user,
					},
				},
				{
					{
						PullRequestLinks: &prlinks,
						Number:           intp(3),
						User:             &user,
					},
				},
				{
					{
						PullRequestLinks: &prlinks,
						Number:           intp(4),
						User:             &user,
					},
				},
			},
			PRs: map[int]github.PullRequest{
				2: PR(2, false),
				3: PR(3, true),
				4: PR(4, false),
			},
			Pages:    []int{4, 4, 4, 0},
			ValidPRs: 2,
		},
		{
			Issues: [][]github.Issue{
				{
					{
						PullRequestLinks: &prlinks,
						Number:           intp(1),
						User:             &user,
					},
				},
				{
					{
						PullRequestLinks: &prlinks,
						Number:           intp(2),
						User:             &user,
					},
				},
				{
					{
						PullRequestLinks: &prlinks,
						Number:           intp(3),
						User:             &user,
					},
					{
						PullRequestLinks: &prlinks,
						Number:           intp(4),
						User:             &user,
					},
					{
						PullRequestLinks: &prlinks,
						Number:           intp(5),
						User:             &user,
					},
				},
			},
			PRs: map[int]github.PullRequest{
				1: PR(1, false),
				2: PR(2, false),
				3: PR(3, false),
				4: PR(4, true),
				5: PR(5, false),
			},
			Pages:    []int{3, 3, 0},
			ValidPRs: 4,
		},
	}

	for _, test := range tests {
		client, server, mux := github_test.InitTest()
		config := &Config{
			client:      client,
			Org:         "foo",
			Project:     "bar",
			MaxPRNumber: 32768,
		}
		mux.HandleFunc("/repos/foo/bar/pulls/", func(w http.ResponseWriter, r *http.Request) {
			if r.Method != "GET" {
				t.Errorf("Unexpected method: %s", r.Method)
			}
			prNumS := strings.TrimPrefix(r.URL.Path, "/repos/foo/bar/pulls/")
			prNum, _ := strconv.Atoi(prNumS)
			data, err := json.Marshal(test.PRs[prNum])
			if err != nil {
				t.Errorf("Unexpected error: %v", err)
			}

			w.Write(data)
		})
		count := 0
		mux.HandleFunc("/repos/foo/bar/issues", func(w http.ResponseWriter, r *http.Request) {
			if r.Method != "GET" {
				t.Errorf("Unexpected method: %s", r.Method)
			}
			// this means page 0, return page 1
			page := r.URL.Query().Get("page")
			if page == "" {
				t.Errorf("Should not get page 0, start with page 1")
			}
			if page != strconv.Itoa(count+1) {
				t.Errorf("Unexpected page: %s", r.URL.Query().Get("page"))
			}
			if r.URL.Query().Get("sort") != "created" {
				t.Errorf("Unexpected sort: %s", r.URL.Query().Get("sort"))
			}
			if r.URL.Query().Get("per_page") != "20" {
				t.Errorf("Unexpected per_page: %s", r.URL.Query().Get("per_page"))
			}
			w.Header().Add("Link",
				fmt.Sprintf("<https://api.github.com/?page=%d>; rel=\"last\"", test.Pages[count]))
			w.WriteHeader(http.StatusOK)
			data, err := json.Marshal(test.Issues[count])
			if err != nil {
				t.Errorf("Unexpected error: %v", err)
			}

			w.Write(data)
			count++
		})
		prs := []*github.PullRequest{}
		handle := func(pr *github.PullRequest, issue *github.Issue) error {
			prs = append(prs, pr)
			return nil
		}
		err := config.ForEachPRDo([]string{}, handle)
		if err != nil {
			t.Errorf("unexpected error: %v", err)
		}
		if len(prs) != test.ValidPRs {
			t.Errorf("unexpected output %d vs %d", len(prs), test.ValidPRs)
		}

		if count != len(test.Issues) {
			t.Errorf("unexpected number of fetches: %d", count)
		}
		server.Close()
	}
}