github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/plugins/dco/dco_test.go (about)

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package dco
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"testing"
    23  
    24  	"github.com/sirupsen/logrus"
    25  
    26  	"k8s.io/test-infra/prow/github"
    27  	"k8s.io/test-infra/prow/github/fakegithub"
    28  )
    29  
    30  type fakePruner struct{}
    31  
    32  func (fp *fakePruner) PruneComments(shouldPrune func(github.IssueComment) bool) {}
    33  
    34  func strP(str string) *string {
    35  	return &str
    36  }
    37  
    38  func TestHandlePullRequest(t *testing.T) {
    39  	var testcases = []struct {
    40  		// test settings
    41  		name string
    42  
    43  		// PR settings
    44  		pullRequestEvent github.PullRequestEvent
    45  		commits          []github.RepositoryCommit
    46  		issueState       string
    47  		hasDCOYes        bool
    48  		hasDCONo         bool
    49  		// status of the DCO github context
    50  		status string
    51  
    52  		// expectations
    53  		addedLabel     string
    54  		removedLabel   string
    55  		expectedStatus string
    56  		// org/repo#number:body
    57  		addedComment string
    58  		// org/repo#issuecommentid
    59  		removedComment string
    60  	}{
    61  		{
    62  			name: "should not do anything on pull request edited",
    63  			pullRequestEvent: github.PullRequestEvent{
    64  				Action:      github.PullRequestActionEdited,
    65  				PullRequest: github.PullRequest{Number: 3, Head: github.PullRequestBranch{SHA: "sha"}},
    66  			},
    67  		},
    68  		{
    69  			name: "should add 'no' label & status context and add a comment if no commits have sign off",
    70  			pullRequestEvent: github.PullRequestEvent{
    71  				Action:      github.PullRequestActionOpened,
    72  				PullRequest: github.PullRequest{Number: 3, Head: github.PullRequestBranch{SHA: "sha"}},
    73  			},
    74  			commits: []github.RepositoryCommit{
    75  				{SHA: "sha", Commit: github.GitCommit{Message: "not a sign off"}},
    76  			},
    77  			issueState: "open",
    78  			hasDCONo:   false,
    79  			hasDCOYes:  false,
    80  
    81  			addedLabel:     fmt.Sprintf("/#3:%s", dcoNoLabel),
    82  			expectedStatus: github.StatusFailure,
    83  			addedComment: `/#3:Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits.
    84  
    85  :memo: **Please follow instructions in the [contributing guide](https://github.com///blob/master/CONTRIBUTING.md) to update your commits with the DCO**
    86  
    87  Full details of the Developer Certificate of Origin can be found at [developercertificate.org](https://developercertificate.org/).
    88  
    89  **The list of commits missing DCO signoff**:
    90  
    91  - [sha](https://github.com///commits/sha) not a sign off
    92  
    93  <details>
    94  
    95  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
    96  </details>
    97  `,
    98  		},
    99  		{
   100  			name: "should add 'no' label & status context, remove old labels and add a comment if no commits have sign off",
   101  			pullRequestEvent: github.PullRequestEvent{
   102  				Action:      github.PullRequestActionOpened,
   103  				PullRequest: github.PullRequest{Number: 3, Head: github.PullRequestBranch{SHA: "sha"}},
   104  			},
   105  			commits: []github.RepositoryCommit{
   106  				{SHA: "sha", Commit: github.GitCommit{Message: "not a sign off"}},
   107  			},
   108  			issueState: "open",
   109  			hasDCONo:   false,
   110  			hasDCOYes:  true,
   111  
   112  			addedLabel:     fmt.Sprintf("/#3:%s", dcoNoLabel),
   113  			removedLabel:   fmt.Sprintf("/#3:%s", dcoYesLabel),
   114  			expectedStatus: github.StatusFailure,
   115  			addedComment: `/#3:Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits.
   116  
   117  :memo: **Please follow instructions in the [contributing guide](https://github.com///blob/master/CONTRIBUTING.md) to update your commits with the DCO**
   118  
   119  Full details of the Developer Certificate of Origin can be found at [developercertificate.org](https://developercertificate.org/).
   120  
   121  **The list of commits missing DCO signoff**:
   122  
   123  - [sha](https://github.com///commits/sha) not a sign off
   124  
   125  <details>
   126  
   127  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
   128  </details>
   129  `,
   130  		},
   131  		{
   132  			name: "should update comment if labels and status are up to date and sign off is failing",
   133  			pullRequestEvent: github.PullRequestEvent{
   134  				Action:      github.PullRequestActionOpened,
   135  				PullRequest: github.PullRequest{Number: 3, Head: github.PullRequestBranch{SHA: "sha"}},
   136  			},
   137  			commits: []github.RepositoryCommit{
   138  				{SHA: "sha", Commit: github.GitCommit{Message: "not a sign off"}},
   139  			},
   140  			issueState: "open",
   141  			hasDCONo:   true,
   142  			hasDCOYes:  false,
   143  			status:     github.StatusFailure,
   144  
   145  			expectedStatus: github.StatusFailure,
   146  			addedComment: `/#3:Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits.
   147  
   148  :memo: **Please follow instructions in the [contributing guide](https://github.com///blob/master/CONTRIBUTING.md) to update your commits with the DCO**
   149  
   150  Full details of the Developer Certificate of Origin can be found at [developercertificate.org](https://developercertificate.org/).
   151  
   152  **The list of commits missing DCO signoff**:
   153  
   154  - [sha](https://github.com///commits/sha) not a sign off
   155  
   156  <details>
   157  
   158  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
   159  </details>
   160  `,
   161  		},
   162  		{
   163  			name: "should mark the PR as failed if just one commit is missing sign-off",
   164  			pullRequestEvent: github.PullRequestEvent{
   165  				Action:      github.PullRequestActionOpened,
   166  				PullRequest: github.PullRequest{Number: 3, Head: github.PullRequestBranch{SHA: "sha"}},
   167  			},
   168  			commits: []github.RepositoryCommit{
   169  				{SHA: "sha1", Commit: github.GitCommit{Message: "Signed-off-by: someone"}},
   170  				{SHA: "sha", Commit: github.GitCommit{Message: "not signed off"}},
   171  			},
   172  			issueState: "open",
   173  			hasDCONo:   false,
   174  			hasDCOYes:  true,
   175  
   176  			addedLabel:     fmt.Sprintf("/#3:%s", dcoNoLabel),
   177  			removedLabel:   fmt.Sprintf("/#3:%s", dcoYesLabel),
   178  			expectedStatus: github.StatusFailure,
   179  			addedComment: `/#3:Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits.
   180  
   181  :memo: **Please follow instructions in the [contributing guide](https://github.com///blob/master/CONTRIBUTING.md) to update your commits with the DCO**
   182  
   183  Full details of the Developer Certificate of Origin can be found at [developercertificate.org](https://developercertificate.org/).
   184  
   185  **The list of commits missing DCO signoff**:
   186  
   187  - [sha](https://github.com///commits/sha) not signed off
   188  
   189  <details>
   190  
   191  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
   192  </details>
   193  `,
   194  		},
   195  		{
   196  			name: "should add label and update status context if all commits are signed-off",
   197  			pullRequestEvent: github.PullRequestEvent{
   198  				Action:      github.PullRequestActionOpened,
   199  				PullRequest: github.PullRequest{Number: 3, Head: github.PullRequestBranch{SHA: "sha"}},
   200  			},
   201  			commits: []github.RepositoryCommit{
   202  				{SHA: "sha", Commit: github.GitCommit{Message: "Signed-off-by: someone"}},
   203  			},
   204  			issueState: "open",
   205  			hasDCONo:   false,
   206  			hasDCOYes:  false,
   207  
   208  			addedLabel:     fmt.Sprintf("/#3:%s", dcoYesLabel),
   209  			expectedStatus: github.StatusSuccess,
   210  		},
   211  		{
   212  			name: "should add label and update status context and remove old labels if all commits are signed-off",
   213  			pullRequestEvent: github.PullRequestEvent{
   214  				Action:      github.PullRequestActionOpened,
   215  				PullRequest: github.PullRequest{Number: 3, Head: github.PullRequestBranch{SHA: "sha"}},
   216  			},
   217  			commits: []github.RepositoryCommit{
   218  				{SHA: "sha", Commit: github.GitCommit{Message: "Signed-off-by: someone"}},
   219  			},
   220  			issueState: "open",
   221  			hasDCONo:   true,
   222  			hasDCOYes:  false,
   223  
   224  			addedLabel:     fmt.Sprintf("/#3:%s", dcoYesLabel),
   225  			removedLabel:   fmt.Sprintf("/#3:%s", dcoNoLabel),
   226  			expectedStatus: github.StatusSuccess,
   227  		},
   228  	}
   229  	for _, tc := range testcases {
   230  		t.Run(tc.name, func(t *testing.T) {
   231  			fc := &fakegithub.FakeClient{
   232  				CreatedStatuses: make(map[string][]github.Status),
   233  				PullRequests:    map[int]*github.PullRequest{tc.pullRequestEvent.PullRequest.Number: &tc.pullRequestEvent.PullRequest},
   234  				IssueComments:   make(map[int][]github.IssueComment),
   235  				CommitMap: map[string][]github.RepositoryCommit{
   236  					"/#3": tc.commits,
   237  				},
   238  			}
   239  			if tc.hasDCOYes {
   240  				fc.IssueLabelsAdded = append(fc.IssueLabelsAdded, fmt.Sprintf("/#3:%s", dcoYesLabel))
   241  			}
   242  			if tc.hasDCONo {
   243  				fc.IssueLabelsAdded = append(fc.IssueLabelsAdded, fmt.Sprintf("/#3:%s", dcoNoLabel))
   244  			}
   245  			if tc.status != "" {
   246  				fc.CreatedStatuses["sha"] = []github.Status{
   247  					{Context: dcoContextName, State: tc.status},
   248  				}
   249  			}
   250  			if err := handlePullRequest(fc, &fakePruner{}, logrus.WithField("plugin", pluginName), tc.pullRequestEvent); err != nil {
   251  				t.Errorf("For case %s, didn't expect error from dco plugin: %v", tc.name, err)
   252  			}
   253  			ok := tc.addedLabel == ""
   254  			if !ok {
   255  				for _, label := range fc.IssueLabelsAdded {
   256  					if reflect.DeepEqual(tc.addedLabel, label) {
   257  						ok = true
   258  						break
   259  					}
   260  				}
   261  			}
   262  			if !ok {
   263  				t.Errorf("Expected to add: %#v, Got %#v in case %s.", tc.addedLabel, fc.IssueLabelsAdded, tc.name)
   264  			}
   265  			ok = tc.removedLabel == ""
   266  			if !ok {
   267  				for _, label := range fc.IssueLabelsRemoved {
   268  					if reflect.DeepEqual(tc.removedLabel, label) {
   269  						ok = true
   270  						break
   271  					}
   272  				}
   273  			}
   274  			if !ok {
   275  				t.Errorf("Expected to remove: %#v, Got %#v in case %s.", tc.removedLabel, fc.IssueLabelsRemoved, tc.name)
   276  			}
   277  
   278  			// check status is set as expected
   279  			statuses := fc.CreatedStatuses["sha"]
   280  			if len(statuses) == 0 && tc.expectedStatus != "" {
   281  				t.Errorf("Expected dco status to be %q, but it was not set", tc.expectedStatus)
   282  			}
   283  			found := false
   284  			for _, s := range statuses {
   285  				if s.Context == dcoContextName {
   286  					found = true
   287  					if s.State != tc.expectedStatus {
   288  						t.Errorf("Expected dco status to be %q but it was %q", tc.expectedStatus, s.State)
   289  					}
   290  				}
   291  			}
   292  			if !found && tc.expectedStatus != "" {
   293  				t.Errorf("Expect dco status to be %q, but it was not found", tc.expectedStatus)
   294  			}
   295  
   296  			comments := fc.IssueCommentsAdded
   297  			if len(comments) == 0 && tc.addedComment != "" {
   298  				t.Errorf("Expected comment with body %q to be added, but it was not", tc.addedComment)
   299  				return
   300  			}
   301  			if len(comments) > 1 {
   302  				t.Errorf("did not expect more than one comment to be created")
   303  			}
   304  			if len(comments) != 0 && comments[0] != tc.addedComment {
   305  				t.Errorf("expected comment to be %q but it was %q", tc.addedComment, comments[0])
   306  			}
   307  		})
   308  	}
   309  }
   310  func TestHandleComment(t *testing.T) {
   311  	var testcases = []struct {
   312  		// test settings
   313  		name string
   314  
   315  		// PR settings
   316  		commentEvent github.GenericCommentEvent
   317  		pullRequests map[int]*github.PullRequest
   318  		commits      []github.RepositoryCommit
   319  		issueState   string
   320  		hasDCOYes    bool
   321  		hasDCONo     bool
   322  		// status of the DCO github context
   323  		status string
   324  
   325  		// expectations
   326  		addedLabel     string
   327  		removedLabel   string
   328  		expectedStatus string
   329  		// org/repo#number:body
   330  		addedComment string
   331  		// org/repo#issuecommentid
   332  		removedComment string
   333  	}{
   334  		{
   335  			name: "should not do anything if comment does not match /check-dco",
   336  			commentEvent: github.GenericCommentEvent{
   337  				IssueState: "open",
   338  				Action:     github.GenericCommentActionCreated,
   339  				Body:       "not-the-trigger",
   340  				IsPR:       true,
   341  				Number:     3,
   342  			},
   343  			pullRequests: map[int]*github.PullRequest{
   344  				3: {Number: 3, Head: github.PullRequestBranch{SHA: "sha"}},
   345  			},
   346  		},
   347  		{
   348  			name: "should add 'no' label & status context and add a comment if no commits have sign off",
   349  			commentEvent: github.GenericCommentEvent{
   350  				IssueState: "open",
   351  				Action:     github.GenericCommentActionCreated,
   352  				Body:       "/check-dco",
   353  				IsPR:       true,
   354  				Number:     3,
   355  			},
   356  			pullRequests: map[int]*github.PullRequest{
   357  				3: {Number: 3, Head: github.PullRequestBranch{SHA: "sha"}},
   358  			},
   359  			commits: []github.RepositoryCommit{
   360  				{SHA: "sha", Commit: github.GitCommit{Message: "not a sign off"}},
   361  			},
   362  			issueState: "open",
   363  			hasDCONo:   false,
   364  			hasDCOYes:  false,
   365  
   366  			addedLabel:     fmt.Sprintf("/#3:%s", dcoNoLabel),
   367  			expectedStatus: github.StatusFailure,
   368  			addedComment: `/#3:Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits.
   369  
   370  :memo: **Please follow instructions in the [contributing guide](https://github.com///blob/master/CONTRIBUTING.md) to update your commits with the DCO**
   371  
   372  Full details of the Developer Certificate of Origin can be found at [developercertificate.org](https://developercertificate.org/).
   373  
   374  **The list of commits missing DCO signoff**:
   375  
   376  - [sha](https://github.com///commits/sha) not a sign off
   377  
   378  <details>
   379  
   380  Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
   381  </details>
   382  `,
   383  		},
   384  	}
   385  	for _, tc := range testcases {
   386  		t.Run(tc.name, func(t *testing.T) {
   387  			fc := &fakegithub.FakeClient{
   388  				CreatedStatuses: make(map[string][]github.Status),
   389  				PullRequests:    tc.pullRequests,
   390  				IssueComments:   make(map[int][]github.IssueComment),
   391  				CommitMap: map[string][]github.RepositoryCommit{
   392  					"/#3": tc.commits,
   393  				},
   394  			}
   395  			if tc.hasDCOYes {
   396  				fc.IssueLabelsAdded = append(fc.IssueLabelsAdded, fmt.Sprintf("/#3:%s", dcoYesLabel))
   397  			}
   398  			if tc.hasDCONo {
   399  				fc.IssueLabelsAdded = append(fc.IssueLabelsAdded, fmt.Sprintf("/#3:%s", dcoNoLabel))
   400  			}
   401  			if tc.status != "" {
   402  				fc.CreatedStatuses["sha"] = []github.Status{
   403  					{Context: dcoContextName, State: tc.status},
   404  				}
   405  			}
   406  			if err := handleComment(fc, &fakePruner{}, logrus.WithField("plugin", pluginName), tc.commentEvent); err != nil {
   407  				t.Errorf("For case %s, didn't expect error from dco plugin: %v", tc.name, err)
   408  			}
   409  			ok := tc.addedLabel == ""
   410  			if !ok {
   411  				for _, label := range fc.IssueLabelsAdded {
   412  					if reflect.DeepEqual(tc.addedLabel, label) {
   413  						ok = true
   414  						break
   415  					}
   416  				}
   417  			}
   418  			if !ok {
   419  				t.Errorf("Expected to add: %#v, Got %#v in case %s.", tc.addedLabel, fc.IssueLabelsAdded, tc.name)
   420  			}
   421  			ok = tc.removedLabel == ""
   422  			if !ok {
   423  				for _, label := range fc.IssueLabelsRemoved {
   424  					if reflect.DeepEqual(tc.removedLabel, label) {
   425  						ok = true
   426  						break
   427  					}
   428  				}
   429  			}
   430  			if !ok {
   431  				t.Errorf("Expected to remove: %#v, Got %#v in case %s.", tc.removedLabel, fc.IssueLabelsRemoved, tc.name)
   432  			}
   433  
   434  			// check status is set as expected
   435  			statuses := fc.CreatedStatuses["sha"]
   436  			if len(statuses) == 0 && tc.expectedStatus != "" {
   437  				t.Errorf("Expected dco status to be %q, but it was not set", tc.expectedStatus)
   438  			}
   439  			found := false
   440  			for _, s := range statuses {
   441  				if s.Context == dcoContextName {
   442  					found = true
   443  					if s.State != tc.expectedStatus {
   444  						t.Errorf("Expected dco status to be %q but it was %q", tc.expectedStatus, s.State)
   445  					}
   446  				}
   447  			}
   448  			if !found && tc.expectedStatus != "" {
   449  				t.Errorf("Expect dco status to be %q, but it was not found", tc.expectedStatus)
   450  			}
   451  
   452  			comments := fc.IssueCommentsAdded
   453  			if len(comments) == 0 && tc.addedComment != "" {
   454  				t.Errorf("Expected comment with body %q to be added, but it was not", tc.addedComment)
   455  				return
   456  			}
   457  			if len(comments) > 1 {
   458  				t.Errorf("did not expect more than one comment to be created")
   459  			}
   460  			if len(comments) != 0 && comments[0] != tc.addedComment {
   461  				t.Errorf("expected comment to be %q but it was %q", tc.addedComment, comments[0])
   462  			}
   463  		})
   464  	}
   465  }