sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/plugins/invalidcommitmsg/invalidcommitmsg_test.go (about)

     1  /*
     2  Copyright 2019 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 invalidcommitmsg
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"testing"
    23  
    24  	"github.com/google/go-cmp/cmp"
    25  	"github.com/sirupsen/logrus"
    26  
    27  	"sigs.k8s.io/prow/pkg/github"
    28  	"sigs.k8s.io/prow/pkg/github/fakegithub"
    29  )
    30  
    31  type fakePruner struct{}
    32  
    33  func (fp *fakePruner) PruneComments(shouldPrune func(github.IssueComment) bool) {}
    34  
    35  func makeFakePullRequestEvent(action github.PullRequestEventAction, title string) github.PullRequestEvent {
    36  	return github.PullRequestEvent{
    37  		Action: action,
    38  		Number: 3,
    39  		Repo: github.Repo{
    40  			Owner: github.User{
    41  				Login: "k",
    42  			},
    43  			Name: "k",
    44  		},
    45  		PullRequest: github.PullRequest{
    46  			Title: title,
    47  		},
    48  	}
    49  }
    50  
    51  var invalidCommitComment = `k/k#3:[Keywords](https://help.github.com/articles/closing-issues-using-keywords) which can automatically close issues and at(@) or hashtag(#) mentions are not allowed in commit messages.
    52  
    53  **The list of commits with invalid commit messages**:
    54  
    55  - [sha1](https://github.com/k/k/commits/sha1) this is a @mention
    56  - [sha2](https://github.com/k/k/commits/sha2) this @menti-on has a hyphen
    57  - [sha3](https://github.com/k/k/commits/sha3) this @Menti-On has mixed case letters
    58  - [sha4](https://github.com/k/k/commits/sha4) fixes k/k#9999
    59  - [sha5](https://github.com/k/k/commits/sha5) Close k/k#9999
    60  - [sha6](https://github.com/k/k/commits/sha6) resolved k/k#9999
    61  
    62  <details>
    63  
    64  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-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
    65  </details>
    66  `
    67  
    68  var invalidPRTitleComment = `k/k#3:[Keywords](https://help.github.com/articles/closing-issues-using-keywords) which can automatically close issues and at(@) mentions are not allowed in the title of a Pull Request.
    69  
    70  You can edit the title by writing **/retitle <new-title>** in a comment.
    71  
    72  <details>
    73  When GitHub merges a Pull Request, the title is included in the merge commit. To avoid invalid keywords in the merge commit, please edit the title of the PR.
    74  
    75  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-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
    76  </details>
    77  `
    78  
    79  func TestHandlePullRequest(t *testing.T) {
    80  	var testcases = []struct {
    81  		name string
    82  
    83  		// PR settings
    84  		action                       github.PullRequestEventAction
    85  		commits                      []github.RepositoryCommit
    86  		title                        string
    87  		hasInvalidCommitMessageLabel bool
    88  
    89  		// expectations
    90  		addedLabel    string
    91  		removedLabel  string
    92  		addedComments []string
    93  	}{
    94  		{
    95  			name:   "unsupported PR action -> no-op",
    96  			action: github.PullRequestActionLabeled,
    97  		},
    98  		{
    99  			name:   "contains valid message -> no-op",
   100  			action: github.PullRequestActionOpened,
   101  			commits: []github.RepositoryCommit{
   102  				{SHA: "sha1", Commit: github.GitCommit{Message: "this is a valid message"}},
   103  				{SHA: "sha2", Commit: github.GitCommit{Message: "fixing k/k#9999"}},
   104  				{SHA: "sha3", Commit: github.GitCommit{Message: "not a @ mention"}},
   105  				{SHA: "sha4", Commit: github.GitCommit{Message: "escape @\u200bmention with zero width unicode"}},
   106  			},
   107  			hasInvalidCommitMessageLabel: false,
   108  		},
   109  		{
   110  			name:   "msg contains invalid keywords -> add label and comment",
   111  			action: github.PullRequestActionOpened,
   112  			commits: []github.RepositoryCommit{
   113  				{SHA: "sha1", Commit: github.GitCommit{Message: "this is a @mention"}},
   114  				{SHA: "sha2", Commit: github.GitCommit{Message: "this @menti-on has a hyphen"}},
   115  				{SHA: "sha3", Commit: github.GitCommit{Message: "this @Menti-On has mixed case letters"}},
   116  				{SHA: "sha4", Commit: github.GitCommit{Message: "fixes k/k#9999"}},
   117  				{SHA: "sha5", Commit: github.GitCommit{Message: "Close k/k#9999"}},
   118  				{SHA: "sha6", Commit: github.GitCommit{Message: "resolved k/k#9999"}},
   119  				{SHA: "sha7", Commit: github.GitCommit{Message: "this is an email@address and is valid"}},
   120  			},
   121  			hasInvalidCommitMessageLabel: false,
   122  
   123  			addedLabel:    fmt.Sprintf("k/k#3:%s", invalidCommitMsgLabel),
   124  			addedComments: []string{invalidCommitComment},
   125  		},
   126  		{
   127  			name:   "msg does not contain invalid keywords but has label -> remove label",
   128  			action: github.PullRequestActionOpened,
   129  			commits: []github.RepositoryCommit{
   130  				{SHA: "sha", Commit: github.GitCommit{Message: "this is a valid message"}},
   131  			},
   132  			hasInvalidCommitMessageLabel: true,
   133  
   134  			removedLabel: fmt.Sprintf("k/k#3:%s", invalidCommitMsgLabel),
   135  		},
   136  		{
   137  			name:   "contains valid title -> no-op",
   138  			action: github.PullRequestActionOpened,
   139  			commits: []github.RepositoryCommit{
   140  				{SHA: "sha1", Commit: github.GitCommit{Message: "this is a valid message"}},
   141  			},
   142  			title:                        "valid title",
   143  			hasInvalidCommitMessageLabel: false,
   144  		},
   145  		{
   146  			name:   "contains invalid title with @mention -> add label and comment",
   147  			action: github.PullRequestActionOpened,
   148  			commits: []github.RepositoryCommit{
   149  				{SHA: "sha1", Commit: github.GitCommit{Message: "this is a valid message"}},
   150  			},
   151  			title:                        "title with @mention",
   152  			hasInvalidCommitMessageLabel: false,
   153  			addedLabel:                   fmt.Sprintf("k/k#3:%s", invalidCommitMsgLabel),
   154  			addedComments:                []string{invalidPRTitleComment},
   155  		},
   156  		{
   157  			name:   "contains invalid title with fixes keyword -> add label and comment",
   158  			action: github.PullRequestActionOpened,
   159  			commits: []github.RepositoryCommit{
   160  				{SHA: "sha1", Commit: github.GitCommit{Message: "this is a valid message"}},
   161  			},
   162  			title:                        "fixes #9999",
   163  			hasInvalidCommitMessageLabel: false,
   164  			addedLabel:                   fmt.Sprintf("k/k#3:%s", invalidCommitMsgLabel),
   165  			addedComments:                []string{invalidPRTitleComment},
   166  		},
   167  		{
   168  			name:   "contains invalid title and invalid commits -> add label and 2 comments",
   169  			action: github.PullRequestActionOpened,
   170  			commits: []github.RepositoryCommit{
   171  				{SHA: "sha1", Commit: github.GitCommit{Message: "this is a @mention"}},
   172  				{SHA: "sha2", Commit: github.GitCommit{Message: "this @menti-on has a hyphen"}},
   173  				{SHA: "sha3", Commit: github.GitCommit{Message: "this @Menti-On has mixed case letters"}},
   174  				{SHA: "sha4", Commit: github.GitCommit{Message: "fixes k/k#9999"}},
   175  				{SHA: "sha5", Commit: github.GitCommit{Message: "Close k/k#9999"}},
   176  				{SHA: "sha6", Commit: github.GitCommit{Message: "resolved k/k#9999"}},
   177  				{SHA: "sha7", Commit: github.GitCommit{Message: "this is an email@address and is valid"}},
   178  			},
   179  			title:                        "title with @mention",
   180  			hasInvalidCommitMessageLabel: false,
   181  			addedLabel:                   fmt.Sprintf("k/k#3:%s", invalidCommitMsgLabel),
   182  			addedComments:                []string{invalidCommitComment, invalidPRTitleComment},
   183  		},
   184  		{
   185  			name:   "valid commits and invalid title, and has label -> keep label and add comment",
   186  			action: github.PullRequestActionOpened,
   187  			commits: []github.RepositoryCommit{
   188  				{SHA: "sha", Commit: github.GitCommit{Message: "this is a valid message"}},
   189  			},
   190  			title:                        "title with @mention",
   191  			hasInvalidCommitMessageLabel: true,
   192  			addedComments:                []string{invalidPRTitleComment},
   193  		},
   194  		{
   195  			name:   "invalid commits and valid title, and has label -> keep label and add comment",
   196  			action: github.PullRequestActionOpened,
   197  			commits: []github.RepositoryCommit{
   198  				{SHA: "sha1", Commit: github.GitCommit{Message: "this is a @mention"}},
   199  				{SHA: "sha2", Commit: github.GitCommit{Message: "this @menti-on has a hyphen"}},
   200  				{SHA: "sha3", Commit: github.GitCommit{Message: "this @Menti-On has mixed case letters"}},
   201  				{SHA: "sha4", Commit: github.GitCommit{Message: "fixes k/k#9999"}},
   202  				{SHA: "sha5", Commit: github.GitCommit{Message: "Close k/k#9999"}},
   203  				{SHA: "sha6", Commit: github.GitCommit{Message: "resolved k/k#9999"}},
   204  				{SHA: "sha7", Commit: github.GitCommit{Message: "this is an email@address and is valid"}},
   205  			},
   206  			title:                        "valid title",
   207  			hasInvalidCommitMessageLabel: true,
   208  			addedComments:                []string{invalidCommitComment},
   209  		},
   210  		{
   211  			name:   "valid title and valid commits, and has label -> remove label",
   212  			action: github.PullRequestActionOpened,
   213  			commits: []github.RepositoryCommit{
   214  				{SHA: "sha", Commit: github.GitCommit{Message: "this is a valid message"}},
   215  			},
   216  			title:                        "valid title",
   217  			hasInvalidCommitMessageLabel: true,
   218  			removedLabel:                 fmt.Sprintf("k/k#3:%s", invalidCommitMsgLabel),
   219  		},
   220  	}
   221  
   222  	for _, tc := range testcases {
   223  		t.Run(tc.name, func(t *testing.T) {
   224  			title := "fake title"
   225  			if tc.title != "" {
   226  				title = tc.title
   227  			}
   228  
   229  			event := makeFakePullRequestEvent(tc.action, title)
   230  			fc := fakegithub.NewFakeClient()
   231  			fc.PullRequests = map[int]*github.PullRequest{event.Number: &event.PullRequest}
   232  			fc.IssueComments = make(map[int][]github.IssueComment)
   233  			fc.CommitMap = map[string][]github.RepositoryCommit{
   234  				"k/k#3": tc.commits,
   235  			}
   236  
   237  			if tc.hasInvalidCommitMessageLabel {
   238  				fc.IssueLabelsAdded = append(fc.IssueLabelsAdded, fmt.Sprintf("k/k#3:%s", invalidCommitMsgLabel))
   239  			}
   240  			if err := handle(fc, logrus.WithField("plugin", pluginName), event, &fakePruner{}); err != nil {
   241  				t.Errorf("For case %s, didn't expect error from invalidcommitmsg plugin: %v", tc.name, err)
   242  			}
   243  
   244  			ok := tc.addedLabel == ""
   245  			if !ok {
   246  				for _, label := range fc.IssueLabelsAdded {
   247  					if reflect.DeepEqual(tc.addedLabel, label) {
   248  						ok = true
   249  						break
   250  					}
   251  				}
   252  			}
   253  			if !ok {
   254  				t.Errorf("Expected to add: %#v, Got %#v in case %s.", tc.addedLabel, fc.IssueLabelsAdded, tc.name)
   255  			}
   256  
   257  			ok = tc.removedLabel == ""
   258  			if !ok {
   259  				for _, label := range fc.IssueLabelsRemoved {
   260  					if reflect.DeepEqual(tc.removedLabel, label) {
   261  						ok = true
   262  						break
   263  					}
   264  				}
   265  			}
   266  			if !ok {
   267  				t.Errorf("Expected to remove: %#v, Got %#v in case %s.", tc.removedLabel, fc.IssueLabelsRemoved, tc.name)
   268  			}
   269  
   270  			comments := fc.IssueCommentsAdded
   271  			if len(comments) != len(tc.addedComments) {
   272  				t.Errorf("Expected %v comments, but received %v", len(tc.addedComments), len(comments))
   273  				return
   274  			}
   275  
   276  			if diff := cmp.Diff(comments, tc.addedComments); diff != "" {
   277  				t.Errorf("Actual comments differ from expected comments: %s", diff)
   278  			}
   279  		})
   280  	}
   281  }